mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 08:04:14 -10:00
Compare commits
131
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee9d203d24 | ||
|
|
6489ae7de1 | ||
|
|
c3beb171c2 | ||
|
|
6b43991619 | ||
|
|
c91a2ce822 | ||
|
|
da6c11f259 | ||
|
|
82a9e7fc9a | ||
|
|
e7f03babaa | ||
|
|
0af718beea | ||
|
|
16f96c5add | ||
|
|
84c56f3c4e | ||
|
|
14553f9a5a | ||
|
|
441038a508 | ||
|
|
4bf328edd9 | ||
|
|
f7d5d5f4c6 | ||
|
|
ea764705f1 | ||
|
|
3f00c639dd | ||
|
|
f5c75eba83 | ||
|
|
2a86250fd4 | ||
|
|
c4616ff43c | ||
|
|
c3a28dcf7a | ||
|
|
0417fc16f6 | ||
|
|
901e9fd9e1 | ||
|
|
c5b3250d17 | ||
|
|
9f72364e54 | ||
|
|
b1b6f8186c | ||
|
|
184dde22ad | ||
|
|
0f5262ef87 | ||
|
|
f68932ab3f | ||
|
|
8a669d1d84 | ||
|
|
bb8b7111b7 | ||
|
|
567e51ca46 | ||
|
|
05166e896f | ||
|
|
3cfe6435f3 | ||
|
|
48c65108d6 | ||
|
|
a271932c8a | ||
|
|
90b3c7bca4 | ||
|
|
8c6fe18280 | ||
|
|
97dbae571c | ||
|
|
b5e629c4a6 | ||
|
|
30b1cb8a85 | ||
|
|
eb70ed2d0b | ||
|
|
53593be73f | ||
|
|
a144991ede | ||
|
|
cf5ae8bd07 | ||
|
|
0bb5ea506a | ||
|
|
d76e845bda | ||
|
|
47d1f0d66b | ||
|
|
bf1127f7f2 | ||
|
|
8b58648713 | ||
|
|
15b5b7c09b | ||
|
|
4d3ac9177a | ||
|
|
92a9bc4da9 | ||
|
|
4b3a9dad8a | ||
|
|
d7fcfe8760 | ||
|
|
88ca9cf905 | ||
|
|
c41fc309ad | ||
|
|
a8a36dd6e2 | ||
|
|
0a4dfccf25 | ||
|
|
20df965603 | ||
|
|
7b697ae5d1 | ||
|
|
ab5b319a9d | ||
|
|
fc18744280 | ||
|
|
8c5d55404c | ||
|
|
53c436b7dc | ||
|
|
3bf6ca5527 | ||
|
|
f5b8e3f23f | ||
|
|
3cb03537e5 | ||
|
|
85d23885ae | ||
|
|
6cad9d45d0 | ||
|
|
2810bebd07 | ||
|
|
cf0eb211e4 | ||
|
|
3668cd73fb | ||
|
|
69804ed3b9 | ||
|
|
2d376ce90b | ||
|
|
dbee28263b | ||
|
|
3160c8f31f | ||
|
|
84746926f0 | ||
|
|
020e18d0f8 | ||
|
|
ceca730e6b | ||
|
|
677a079132 | ||
|
|
971f44e4a1 | ||
|
|
d17b842d63 | ||
|
|
032e116a42 | ||
|
|
5cc548589f | ||
|
|
4047b42522 | ||
|
|
a2b039bb51 | ||
|
|
3c7fe8f1c6 | ||
|
|
a1d6d00a6a | ||
|
|
2481b9632f | ||
|
|
58e11d40fa | ||
|
|
196d42c8f1 | ||
|
|
3a4eee20a0 | ||
|
|
7714950206 | ||
|
|
bae0f479b8 | ||
|
|
fd090228e8 | ||
|
|
69dfe8c047 | ||
|
|
d8b9f7cbd6 | ||
|
|
4c6269ba5a | ||
|
|
c98d1e2c17 | ||
|
|
d582987c78 | ||
|
|
794db93ec9 | ||
|
|
338b622b97 | ||
|
|
4ecf42b5c2 | ||
|
|
7c5262d63d | ||
|
|
1a41eacb66 | ||
|
|
32e4ed63e9 | ||
|
|
e65629e089 | ||
|
|
0c14621e53 | ||
|
|
e27e030431 | ||
|
|
1fd850c738 | ||
|
|
4738bf229c | ||
|
|
c0a5a06efc | ||
|
|
97ff802a40 | ||
|
|
106c7f0427 | ||
|
|
37a46a21e0 | ||
|
|
060d1c40fd | ||
|
|
3320f3958b | ||
|
|
643a9ca3fe | ||
|
|
52c2e1b0cf | ||
|
|
0fab805980 | ||
|
|
dd9bb01640 | ||
|
|
0df56e5a95 | ||
|
|
e8a77d83ea | ||
|
|
98de546086 | ||
|
|
9e9ab2042f | ||
|
|
d7d946f17a | ||
|
|
c4555a124a | ||
|
|
65a44da85f | ||
|
|
6b0d6b2784 | ||
|
|
4b1991ad5d |
No files matched your search
@@ -5,4 +5,4 @@ queries:
|
||||
uses: security-extended
|
||||
paths:
|
||||
- server
|
||||
- implant
|
||||
- client
|
||||
@@ -3,7 +3,7 @@ name: Release
|
||||
on:
|
||||
push:
|
||||
tags: ["v[1-9]+.[0-9]+.[0-9]+"]
|
||||
branches: [master, goreleaser]
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
linux-windows-build:
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
- name: Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "1.19.1"
|
||||
go-version: "^1.19"
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
|
||||
@@ -4,23 +4,22 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- test/*
|
||||
# pull_request:
|
||||
# branches:
|
||||
# - master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
name: CodeQL Security Scan
|
||||
# CodeQL runs on ubuntu-latest, windows-latest, and macos-latest
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
timeout-minutes: 90
|
||||
|
||||
steps:
|
||||
# Setup go environment
|
||||
- name: Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "1.19.1"
|
||||
go-version: "^1.19"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
@@ -37,23 +36,6 @@ jobs:
|
||||
git build-essential zlib1g zlib1g-dev wget zip unzip \
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64
|
||||
|
||||
- name: Protoc
|
||||
env:
|
||||
PROTOC_VER: 3.11.4
|
||||
run: |
|
||||
wget -O protoc-${PROTOC_VER}-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& unzip protoc-${PROTOC_VER}-linux-x86_64.zip \
|
||||
&& sudo cp -vv ./bin/protoc /usr/local/bin/protoc
|
||||
|
||||
- name: Protoc-gen-go
|
||||
env:
|
||||
PROTOC_GEN_GO_VER: 1.3.5
|
||||
run: |
|
||||
wget -O protoc-gen-go.tar.gz https://github.com/golang/protobuf/archive/v${PROTOC_GEN_GO_VER}.tar.gz \
|
||||
&& tar xvf protoc-gen-go.tar.gz \
|
||||
&& cd protobuf-${PROTOC_GEN_GO_VER} \
|
||||
&& make install
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
name: Build & Test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- test/*
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
@@ -21,7 +18,7 @@ jobs:
|
||||
- name: Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "1.19.1"
|
||||
go-version: "^1.19"
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -55,7 +52,7 @@ jobs:
|
||||
- name: Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "1.19.1"
|
||||
go-version: "^1.19"
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.19.1
|
||||
FROM golang:1.19.2
|
||||
|
||||
#
|
||||
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
|
||||
|
||||
@@ -99,6 +99,12 @@ ifeq ($(MAKECMDGOALS), linux)
|
||||
-X $(CLIENT_ASSETS_PKG).DefaultArmoryRepoURL=$(ARMORY_REPO_URL)"
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS), linux-arm64)
|
||||
# Redefine TAGS/ENV to use pure go sqlite3
|
||||
TAGS = -tags osusergo,netgo,gosqlite
|
||||
ENV = CGO_ENABLED=0
|
||||
endif
|
||||
|
||||
#
|
||||
# Targets
|
||||
#
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
Sliver
|
||||
======
|
||||
# Sliver
|
||||
|
||||
Sliver is an open source cross-platform adversary emulation/red team framework, it can be used by organizations of all sizes to perform security testing. Sliver's implants support C2 over Mutual TLS (mTLS), WireGuard, HTTP(S), and DNS and are dynamically compiled with per-binary asymmetric encryption keys.
|
||||
|
||||
The server and client support MacOS, Windows, and Linux. Implants are supported on MacOS, Windows, and Linux (and possibly every Golang compiler target but we've not tested them all).
|
||||
|
||||
 [](https://goreportcard.com/report/github.com/BishopFox/sliver) [](https://www.gnu.org/licenses/gpl-3.0)
|
||||
[](https://github.com/BishopFox/sliver/actions/workflows/go.yml) [](https://github.com/BishopFox/sliver/actions/workflows/codeql-scanning.yml) [](https://goreportcard.com/report/github.com/BishopFox/sliver) [](https://www.gnu.org/licenses/gpl-3.0)
|
||||
|
||||
### Features
|
||||
|
||||
* Dynamic code generation
|
||||
* Compile-time obfuscation
|
||||
* Multiplayer-mode
|
||||
* Staged and Stageless payloads
|
||||
* [Procedurally generated C2](https://github.com/BishopFox/sliver/wiki/HTTP(S)-C2#under-the-hood) over HTTP(S)
|
||||
* [DNS canary](https://github.com/BishopFox/sliver/wiki/DNS-C2#dns-canaries) blue team detection
|
||||
* [Secure C2](https://github.com/BishopFox/sliver/wiki/Transport-Encryption) over mTLS, WireGuard, HTTP(S), and DNS
|
||||
* Fully scriptable using [JavaScript/TypeScript](https://github.com/moloch--/sliver-script) or [Python](https://github.com/moloch--/sliver-py)
|
||||
* Windows process migration, process injection, user token manipulation, etc.
|
||||
* Let's Encrypt integration
|
||||
* In-memory .NET assembly execution
|
||||
* COFF/BOF in-memory loader
|
||||
* TCP and named pipe pivots
|
||||
* Much more!
|
||||
- Dynamic code generation
|
||||
- Compile-time obfuscation
|
||||
- Multiplayer-mode
|
||||
- Staged and Stageless payloads
|
||||
- [Procedurally generated C2](<https://github.com/BishopFox/sliver/wiki/HTTP(S)-C2#under-the-hood>) over HTTP(S)
|
||||
- [DNS canary](https://github.com/BishopFox/sliver/wiki/DNS-C2#dns-canaries) blue team detection
|
||||
- [Secure C2](https://github.com/BishopFox/sliver/wiki/Transport-Encryption) over mTLS, WireGuard, HTTP(S), and DNS
|
||||
- Fully scriptable using [JavaScript/TypeScript](https://github.com/moloch--/sliver-script) or [Python](https://github.com/moloch--/sliver-py)
|
||||
- Windows process migration, process injection, user token manipulation, etc.
|
||||
- Let's Encrypt integration
|
||||
- In-memory .NET assembly execution
|
||||
- COFF/BOF in-memory loader
|
||||
- TCP and named pipe pivots
|
||||
- Much more!
|
||||
|
||||
### Getting Started
|
||||
|
||||
@@ -47,4 +46,3 @@ Please take a moment and fill out [our survey](https://forms.gle/SwVsHFNh24ChG58
|
||||
### License - GPLv3
|
||||
|
||||
Sliver is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html), some sub-components may have separate licenses. See their respective subdirectories in this project for details.
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -142,7 +141,7 @@ func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasMan
|
||||
}
|
||||
|
||||
// parse it
|
||||
data, err := ioutil.ReadFile(manifestPath)
|
||||
data, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -303,7 +302,7 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if strings.ToLower(filepath.Ext(binPath)) == ".dll" {
|
||||
isDLL = true
|
||||
}
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -311,7 +310,7 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
var outFilePath *os.File
|
||||
if ctx.Flags.Bool("save") {
|
||||
outFile := filepath.Base(fmt.Sprintf("%s_%s*.log", filepath.Base(ctx.Command.Name), filepath.Base(session.GetHostname())))
|
||||
outFilePath, err = ioutil.TempFile("", outFile)
|
||||
outFilePath, err = os.CreateTemp("", outFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package builders
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// BuildersCmd - List external builders
|
||||
func BuildersCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return
|
||||
}
|
||||
if len(builders.Builders) == 0 {
|
||||
con.PrintInfof("No external builders connected to server\n")
|
||||
} else {
|
||||
PrintBuilders(builders.Builders, con)
|
||||
}
|
||||
}
|
||||
|
||||
func PrintBuilders(externalBuilders []*clientpb.Builder, con *console.SliverConsoleClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"Name", "Operator", "Templates", "Platform", "Compiler Targets",
|
||||
})
|
||||
for _, builder := range externalBuilders {
|
||||
|
||||
targets := []string{}
|
||||
for _, target := range builder.Targets {
|
||||
targets = append(targets, fmt.Sprintf("%s:%s/%s", target.Format, target.GOOS, target.GOARCH))
|
||||
}
|
||||
|
||||
row := table.Row{
|
||||
builder.Name,
|
||||
builder.OperatorName,
|
||||
strings.Join(builder.Templates, ", "),
|
||||
fmt.Sprintf("%s/%s", builder.GOOS, builder.GOARCH),
|
||||
strings.Join(targets, "\n"),
|
||||
}
|
||||
tw.AppendRow(table.Row(row))
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
+121
-29
@@ -41,7 +41,9 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/armory"
|
||||
"github.com/bishopfox/sliver/client/command/backdoor"
|
||||
"github.com/bishopfox/sliver/client/command/beacons"
|
||||
"github.com/bishopfox/sliver/client/command/builders"
|
||||
"github.com/bishopfox/sliver/client/command/completers"
|
||||
"github.com/bishopfox/sliver/client/command/creds"
|
||||
"github.com/bishopfox/sliver/client/command/cursed"
|
||||
"github.com/bishopfox/sliver/client/command/dllhijack"
|
||||
"github.com/bishopfox/sliver/client/command/environment"
|
||||
@@ -422,7 +424,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
LongHelp: help.GetHelpFor([]string{consts.HttpStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("d", "domain", "", "limit responses to specific domain")
|
||||
f.String("w", "website", "", "website name (see websites cmd)")
|
||||
f.String("w", "website", "", "website name (see: websites)")
|
||||
f.String("L", "lhost", "", "interface to bind server to")
|
||||
f.Int("l", "lport", generate.DefaultHTTPLPort, "tcp listen port")
|
||||
f.Bool("D", "disable-otp", false, "disable otp authentication")
|
||||
@@ -447,7 +449,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
LongHelp: help.GetHelpFor([]string{consts.HttpsStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("d", "domain", "", "limit responses to specific domain")
|
||||
f.String("w", "website", "", "website name (see websites cmd)")
|
||||
f.String("w", "website", "", "website name (see: websites)")
|
||||
f.String("L", "lhost", "", "interface to bind server to")
|
||||
f.Int("l", "lport", generate.DefaultHTTPSLPort, "tcp listen port")
|
||||
f.Bool("D", "disable-otp", false, "disable otp authentication")
|
||||
@@ -1208,6 +1210,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
a.Uint("pid", "pid")
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("S", "disable-sgn", true, "disable shikata ga nai shellcode encoder")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
HelpGroup: consts.SliverWinHelpGroup,
|
||||
@@ -1297,6 +1301,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("P", "password", "", "SSH user password")
|
||||
f.String("l", "login", "", "username to use to connect")
|
||||
f.Bool("s", "skip-loot", false, "skip the prompt to use loot credentials")
|
||||
f.String("c", "kerberos-config", "/etc/krb5.conf", "path to remote Kerberos config file")
|
||||
f.String("k", "kerberos-keytab", "", "path to Kerberos keytab file")
|
||||
f.String("r", "kerberos-realm", "", "Kerberos realm")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
@@ -1318,8 +1325,10 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("a", "arch", "amd64", "cpu architecture")
|
||||
f.String("N", "name", "", "agent name")
|
||||
f.Bool("d", "debug", false, "enable debug features")
|
||||
f.Bool("e", "evasion", false, "enable evasion features")
|
||||
f.Bool("e", "evasion", false, "enable evasion features (e.g. overwrite user space hooks)")
|
||||
f.Bool("l", "skip-symbols", false, "skip symbol obfuscation")
|
||||
f.String("I", "template", "sliver", "implant code template")
|
||||
f.Bool("E", "external-builder", false, "use an external builder")
|
||||
|
||||
f.String("c", "canary", "", "canary domain(s)")
|
||||
|
||||
@@ -1347,7 +1356,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
|
||||
f.String("L", "limit-locale", "", "limit execution to hosts that match this locale")
|
||||
|
||||
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
|
||||
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)")
|
||||
f.String("s", "save", "", "directory/file to the binary to")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
@@ -1376,8 +1385,10 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("a", "arch", "amd64", "cpu architecture")
|
||||
f.String("N", "name", "", "agent name")
|
||||
f.Bool("d", "debug", false, "enable debug features")
|
||||
f.Bool("e", "evasion", false, "enable evasion features")
|
||||
f.Bool("e", "evasion", false, "enable evasion features (e.g. overwrite user space hooks)")
|
||||
f.Bool("l", "skip-symbols", false, "skip symbol obfuscation")
|
||||
f.String("I", "template", "sliver", "implant code template")
|
||||
f.Bool("E", "external-builder", false, "use an external builder")
|
||||
|
||||
f.String("c", "canary", "", "canary domain(s)")
|
||||
|
||||
@@ -1405,7 +1416,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
|
||||
f.String("L", "limit-locale", "", "limit execution to hosts that match this locale")
|
||||
|
||||
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
|
||||
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)")
|
||||
f.String("s", "save", "", "directory/file to the binary to")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
@@ -1547,6 +1558,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
|
||||
f.String("Z", "strategy", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)")
|
||||
|
||||
f.String("I", "template", "sliver", "implant code template")
|
||||
|
||||
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
|
||||
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
|
||||
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
|
||||
@@ -1558,7 +1571,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
|
||||
f.String("L", "limit-locale", "", "limit execution to hosts that match this locale")
|
||||
|
||||
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
|
||||
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1611,6 +1624,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
|
||||
f.Bool("R", "run-at-load", false, "run the implant entrypoint from DllMain/Constructor (shared library only)")
|
||||
|
||||
f.String("I", "template", "sliver", "implant code template")
|
||||
|
||||
f.Int("j", "reconnect", generate.DefaultReconnect, "attempt to reconnect every n second(s)")
|
||||
f.Int("P", "poll-timeout", generate.DefaultPollTimeout, "long poll request timeout")
|
||||
f.Int("k", "max-errors", generate.DefaultMaxErrors, "max number of connection errors")
|
||||
@@ -1622,7 +1637,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("F", "limit-fileexists", "", "limit execution to hosts with this file in the filesystem")
|
||||
f.String("L", "limit-locale", "", "limit execution to hosts that match this locale")
|
||||
|
||||
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see `psexec` for more info) and 'shellcode' (windows only)")
|
||||
f.String("f", "format", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -2955,8 +2970,6 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
Help: "Manage the server's loot store",
|
||||
LongHelp: help.GetHelpFor([]string{consts.LootStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("f", "filter", "", "filter based on loot type")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
@@ -2976,7 +2989,6 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("n", "name", "", "name of this piece of loot")
|
||||
f.String("T", "type", "", "force a specific loot type (file/cred)")
|
||||
f.String("F", "file-type", "", "force a specific file type (binary/text)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
@@ -2998,7 +3010,6 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("n", "name", "", "name of this piece of loot")
|
||||
f.String("T", "type", "", "force a specific loot type (file/cred)")
|
||||
f.String("F", "file-type", "", "force a specific file type (binary/text)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
@@ -3011,23 +3022,6 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
lootCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.LootCredsStr,
|
||||
Help: "Add credentials to the server's loot store",
|
||||
LongHelp: help.GetHelpFor([]string{consts.LootStr, consts.LootCredsStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("n", "name", "", "name of this piece of loot")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
loot.LootAddCredentialCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
lootCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.RenameStr,
|
||||
Help: "Re-name a piece of existing loot",
|
||||
@@ -3080,6 +3074,83 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
})
|
||||
con.App.AddCommand(lootCmd)
|
||||
|
||||
// [ Credentials ] ------------------------------------------------------------
|
||||
credsCmd := &grumble.Command{
|
||||
Name: consts.CredsStr,
|
||||
Help: "Manage the database of credentials",
|
||||
LongHelp: help.GetHelpFor([]string{consts.CredsStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
creds.CredsCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
}
|
||||
credsAddCmd := &grumble.Command{
|
||||
Name: consts.AddStr,
|
||||
Help: "Add a credential to the database",
|
||||
LongHelp: help.GetHelpFor([]string{consts.CredsStr, consts.AddStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("u", "username", "", "username for the credential")
|
||||
f.String("p", "plaintext", "", "plaintext for the credential")
|
||||
f.String("P", "hash", "", "hash of the credential")
|
||||
f.String("H", "hash-type", "", "hash type of the credential (see: creds add --help)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
creds.CredsAddCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
}
|
||||
credsAddCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.FileStr,
|
||||
Help: "Add a credential to the database",
|
||||
LongHelp: help.GetHelpFor([]string{consts.CredsStr, consts.AddStr, consts.FileStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("F", "file-format", creds.HashNewlineFormat, "file format of the credential file (see: creds add file --help)")
|
||||
f.String("H", "hash-type", "", "hash type of the credential (see: creds add --help)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("file", "The path of the credential file")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
creds.CredsAddCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
credsCmd.AddCommand(credsAddCmd) // </creds add>
|
||||
credsCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.RmStr,
|
||||
Help: "Remove a credential to the database",
|
||||
LongHelp: help.GetHelpFor([]string{consts.CredsStr, consts.RmStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("i", "id", "", "id of the credential to remove")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
creds.CredsRmCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
con.App.AddCommand(credsCmd)
|
||||
|
||||
// [ Hosts ] --------------------------------------------------------------
|
||||
hostsCmd := &grumble.Command{
|
||||
Name: consts.HostsStr,
|
||||
@@ -3547,4 +3618,25 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
},
|
||||
})
|
||||
con.App.AddCommand(cursedCmd)
|
||||
|
||||
// [ Builders ] ---------------------------------------------
|
||||
buildersCmd := &grumble.Command{
|
||||
Name: consts.BuildersStr,
|
||||
Help: "List external builders",
|
||||
LongHelp: help.GetHelpFor([]string{consts.BuildersStr}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
builders.BuildersCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
con.App.AddCommand(buildersCmd)
|
||||
|
||||
// [ Crackstation ] ------------------------------------------------------------
|
||||
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package creds
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
const (
|
||||
UserColonHashNewlineFormat = "user:hash" // username:hash\n
|
||||
HashNewlineFormat = "hash" // hash\n
|
||||
CSVFormat = "csv" // username,hash\n
|
||||
)
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
username := ctx.Flags.String("username")
|
||||
plaintext := ctx.Flags.String("plaintext")
|
||||
hash := ctx.Flags.String("hash")
|
||||
hashType := parseHashType(ctx.Flags.String("hash-type"))
|
||||
if plaintext == "" && hash == "" {
|
||||
con.PrintErrorf("Either a plaintext or a hash must be provided\n")
|
||||
return
|
||||
}
|
||||
if hashType == clientpb.HashType_INVALID {
|
||||
con.PrintErrorf("Invalid hash type '%s'\n", ctx.Flags.String("hash-type"))
|
||||
return
|
||||
}
|
||||
_, err := con.Rpc.CredsAdd(context.Background(), &clientpb.Credentials{
|
||||
Credentials: []*clientpb.Credential{
|
||||
{
|
||||
Username: username,
|
||||
Plaintext: plaintext,
|
||||
Hash: hash,
|
||||
HashType: hashType,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCreds(creds.Credentials, con)
|
||||
}
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsAddHashFileCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
filePath := ctx.Args.String("file")
|
||||
fileFormat := ctx.Flags.String("file-format")
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
con.PrintErrorf("File '%s' does not exist\n", filePath)
|
||||
return
|
||||
}
|
||||
hashType := parseHashType(ctx.Flags.String("hash-type"))
|
||||
if hashType == clientpb.HashType_INVALID {
|
||||
con.PrintErrorf("Invalid hash type '%s'\n", ctx.Flags.String("hash-type"))
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Parsing file '%s' as '%s' format ...\n", filePath, fileFormat)
|
||||
var creds *clientpb.Credentials
|
||||
var err error
|
||||
switch fileFormat {
|
||||
case UserColonHashNewlineFormat:
|
||||
creds, err = parseUserColonHashNewline(filePath, hashType)
|
||||
case HashNewlineFormat:
|
||||
creds, err = parseHashNewline(filePath, hashType)
|
||||
case CSVFormat:
|
||||
creds, err = parseCSV(filePath, hashType)
|
||||
default:
|
||||
con.PrintErrorf("Invalid file format '%s', see 'creds add file --help'\n", fileFormat)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Adding %d credential(s) ...\n", len(creds.Credentials))
|
||||
_, err = con.Rpc.CredsAdd(context.Background(), creds)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
creds, err = con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
PrintCreds(creds.Credentials, con)
|
||||
}
|
||||
|
||||
func parseHashType(raw string) clientpb.HashType {
|
||||
hashInt, err := strconv.Atoi(raw)
|
||||
if err == nil {
|
||||
return clientpb.HashType(hashInt)
|
||||
}
|
||||
return clientpb.HashType_INVALID
|
||||
}
|
||||
|
||||
func parseUserColonHashNewline(filePath string, hashType clientpb.HashType) (*clientpb.Credentials, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
creds := &clientpb.Credentials{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
creds.Credentials = append(creds.Credentials, &clientpb.Credential{
|
||||
Username: parts[0],
|
||||
Hash: parts[1],
|
||||
HashType: hashType,
|
||||
})
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
func parseHashNewline(filePath string, hashType clientpb.HashType) (*clientpb.Credentials, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
creds := &clientpb.Credentials{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
creds.Credentials = append(creds.Credentials, &clientpb.Credential{
|
||||
Hash: line,
|
||||
HashType: hashType,
|
||||
})
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
func parseCSV(filePath string, hashType clientpb.HashType) (*clientpb.Credentials, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
creds := &clientpb.Credentials{}
|
||||
scanner.Scan() // skip header
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Split(line, ",")
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
creds.Credentials = append(creds.Credentials, &clientpb.Credential{
|
||||
Username: parts[0],
|
||||
Hash: parts[1],
|
||||
HashType: hashType,
|
||||
})
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package creds
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// CredsCmd - Manage credentials
|
||||
func CredsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if len(creds.Credentials) == 0 {
|
||||
con.PrintInfof("No credentials 🙁\n")
|
||||
return
|
||||
}
|
||||
PrintCreds(creds.Credentials, con)
|
||||
}
|
||||
|
||||
func PrintCreds(creds []*clientpb.Credential, con *console.SliverConsoleClient) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Username",
|
||||
"Plaintext",
|
||||
"Hash",
|
||||
"Hash Type",
|
||||
"Cracked",
|
||||
})
|
||||
for _, cred := range creds {
|
||||
tw.AppendRow(table.Row{
|
||||
strings.Split(cred.ID, "-")[0],
|
||||
cred.Username,
|
||||
cred.Plaintext,
|
||||
cred.Hash,
|
||||
cred.HashType,
|
||||
cred.IsCracked,
|
||||
})
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package creds
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CredsCmd - Add new credentials
|
||||
func CredsRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
id := ctx.Flags.String("id")
|
||||
if id == "" {
|
||||
credential, err := SelectCredential(false, clientpb.HashType_INVALID, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
id = credential.ID
|
||||
}
|
||||
_, err := con.Rpc.CredsRm(context.Background(), &clientpb.Credentials{
|
||||
Credentials: []*clientpb.Credential{
|
||||
{
|
||||
ID: id,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if len(creds.Credentials) == 0 {
|
||||
con.PrintInfof("No credentials 🙁\n")
|
||||
return
|
||||
}
|
||||
PrintCreds(creds.Credentials, con)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package creds
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
)
|
||||
|
||||
// SelectCredential - Interactive menu for the user to select a credentials from the database
|
||||
func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.SliverConsoleClient) (*clientpb.Credential, error) {
|
||||
var creds *clientpb.Credentials
|
||||
var err error
|
||||
if hashType == clientpb.HashType_INVALID {
|
||||
creds, err = con.Rpc.Creds(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
creds, err = con.Rpc.GetCredsByHashType(context.Background(), &clientpb.Credential{HashType: hashType})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(creds.Credentials) == 0 {
|
||||
return nil, errors.New("no credentials to select from")
|
||||
}
|
||||
|
||||
// Render selection table
|
||||
buf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
|
||||
for _, cred := range creds.Credentials {
|
||||
if cred.Plaintext == "" {
|
||||
fmt.Fprintf(table, "%s\t%s\t[not cracked]\t\n", strings.Split(cred.ID, "-")[0], cred.HashType)
|
||||
} else {
|
||||
plaintext := cred.Plaintext
|
||||
if len(plaintext) > 12 {
|
||||
plaintext = plaintext[:12] + "..."
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\tplaintext: %s\t\n", strings.Split(cred.ID, "-")[0], cred.HashType, plaintext)
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
options := strings.Split(buf.String(), "\n")
|
||||
options = options[:len(options)-1]
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("no credentials to select from")
|
||||
}
|
||||
|
||||
selected := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a credential:",
|
||||
Options: options,
|
||||
}
|
||||
err = survey.AskOne(prompt, &selected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for index, value := range options {
|
||||
if value == selected {
|
||||
return creds.Credentials[index], nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -20,7 +20,7 @@ package exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -50,7 +50,7 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
}
|
||||
assemblyBytes, err := ioutil.ReadFile(assemblyPath)
|
||||
assemblyBytes, err := os.ReadFile(assemblyPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err.Error())
|
||||
return
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
@@ -52,7 +51,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
}
|
||||
pid := ctx.Flags.Uint("pid")
|
||||
shellcodePath := ctx.Args.String("filepath")
|
||||
shellcodeBin, err := ioutil.ReadFile(shellcodePath)
|
||||
shellcodeBin, err := os.ReadFile(shellcodePath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
@@ -82,7 +81,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
Data: shellcodeBin,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
oldSize := len(shellcodeBin)
|
||||
|
||||
@@ -18,7 +18,6 @@ package exec
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -217,7 +216,7 @@ func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cm
|
||||
lootName = fmt.Sprintf("[%s] %s on %s (%s)", sliverCmdName, shortCommandName, hostName, timeNow)
|
||||
}
|
||||
|
||||
lootMessage := loot.CreateLootMessage(fileName, lootName, clientpb.LootType_LOOT_FILE, clientpb.FileType_TEXT, commandOutput)
|
||||
lootMessage := loot.CreateLootMessage(fileName, lootName, clientpb.FileType_TEXT, commandOutput)
|
||||
loot.SendLootMessage(lootMessage, con)
|
||||
}
|
||||
|
||||
@@ -242,7 +241,7 @@ func SaveExecutionOutput(executionOutput string, commandName string, hostName st
|
||||
|
||||
outFileName := filepath.Base(fmt.Sprintf("%s_%s_%s*.log", commandName, hostName, timeNow))
|
||||
|
||||
outFilePath, err = ioutil.TempFile("", outFileName)
|
||||
outFilePath, err = os.CreateTemp("", outFileName)
|
||||
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -3,14 +3,17 @@ package exec
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -33,12 +36,19 @@ func MigrateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
pid := ctx.Args.Uint("pid")
|
||||
config := con.GetActiveSessionConfig()
|
||||
encoder := clientpb.ShellcodeEncoder_SHIKATA_GA_NAI
|
||||
if ctx.Flags.Bool("disable-sgn") {
|
||||
encoder = clientpb.ShellcodeEncoder_NONE
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Migrating into %d ...", pid), ctrl)
|
||||
|
||||
migrate, err := con.Rpc.Migrate(context.Background(), &clientpb.MigrateReq{
|
||||
Pid: uint32(pid),
|
||||
Config: config,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Encoder: encoder,
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -21,17 +21,19 @@ package exec
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
insecureRand "math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
insecureRand "math/rand"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/generate"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/bishopfox/sliver/server/codenames"
|
||||
"github.com/bishopfox/sliver/util/encoders"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
@@ -93,7 +95,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
serviceBinary, _ = generate.GetSliverBinary(implantProfile, con)
|
||||
} else {
|
||||
// use a custom exe instead of generating a new Sliver
|
||||
fileBytes, err := ioutil.ReadFile(customExe)
|
||||
fileBytes, err := os.ReadFile(customExe)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error reading custom executable '%s'\n", customExe)
|
||||
return
|
||||
@@ -101,7 +103,7 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
serviceBinary = fileBytes
|
||||
}
|
||||
|
||||
filename := randomString(10)
|
||||
filename := randomFileName()
|
||||
filePath := fmt.Sprintf("%s\\%s.exe", uploadPath, filename)
|
||||
uploadGzip := new(encoders.Gzip).Encode(serviceBinary)
|
||||
// upload to remote target
|
||||
@@ -172,11 +174,28 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintInfof("Successfully removed service %s on %s\n", serviceName, hostname)
|
||||
}
|
||||
|
||||
func randomString(length int) string {
|
||||
var charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[insecureRand.Intn(len(charset))]
|
||||
func randomFileName() string {
|
||||
noun, _ := codenames.RandomNoun()
|
||||
noun = strings.ToLower(noun)
|
||||
switch insecureRand.Intn(3) {
|
||||
case 0:
|
||||
noun = strings.ToUpper(noun)
|
||||
case 1:
|
||||
noun = strings.ToTitle(noun)
|
||||
}
|
||||
return string(b)
|
||||
|
||||
separators := []string{"", "", "", "", "", ".", "-", "_", "--", "__"}
|
||||
sep := separators[insecureRand.Intn(len(separators))]
|
||||
|
||||
alphanumeric := "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
prefix := ""
|
||||
for index := 0; index < insecureRand.Intn(3); index++ {
|
||||
prefix += string(alphanumeric[insecureRand.Intn(len(alphanumeric))])
|
||||
}
|
||||
suffix := ""
|
||||
for index := 0; index < insecureRand.Intn(6)+1; index++ {
|
||||
suffix += string(alphanumeric[insecureRand.Intn(len(alphanumeric))])
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%s%s%s%s", prefix, sep, noun, sep, suffix)
|
||||
}
|
||||
@@ -21,7 +21,7 @@ package exec
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -45,7 +45,7 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
processName := ctx.Flags.String("process")
|
||||
args := strings.Join(ctx.Args.StringList("args"), " ")
|
||||
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err.Error())
|
||||
return
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
package exec
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -24,7 +42,7 @@ func SpawnDllCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
processName := ctx.Flags.String("process")
|
||||
exportName := ctx.Flags.String("export")
|
||||
|
||||
binData, err := ioutil.ReadFile(binPath)
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
|
||||
+50
-17
@@ -1,12 +1,29 @@
|
||||
package exec
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/loot"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -31,9 +48,9 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
port := ctx.Flags.Uint("port")
|
||||
privateKeypath := ctx.Flags.String("private-key")
|
||||
if privateKeypath != "" {
|
||||
privKey, err = ioutil.ReadFile(privateKeypath)
|
||||
privateKeyPath := ctx.Flags.String("private-key")
|
||||
if privateKeyPath != "" {
|
||||
privKey, err = os.ReadFile(privateKeyPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
@@ -43,6 +60,19 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
hostname := ctx.Args.String("hostname")
|
||||
command := ctx.Args.StringList("command")
|
||||
kerberosRealm := ctx.Flags.String("kerberos-realm")
|
||||
kerberosConfig := ctx.Flags.String("kerberos-config")
|
||||
kerberosKeytabFile := ctx.Flags.String("kerberos-keytab")
|
||||
|
||||
if kerberosRealm != "" && kerberosKeytabFile == "" {
|
||||
con.PrintErrorf("You must specify a keytab file with the --kerberos-keytab flag\n")
|
||||
return
|
||||
}
|
||||
kerberosKeytab, err := os.ReadFile(kerberosKeytabFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if password == "" && len(privKey) == 0 && !ctx.Flags.Bool("skip-loot") {
|
||||
oldUsername := username
|
||||
@@ -59,6 +89,9 @@ func SSHCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
PrivKey: privKey,
|
||||
Password: password,
|
||||
Command: strings.Join(command, " "),
|
||||
Realm: kerberosRealm,
|
||||
Krb5Conf: kerberosConfig,
|
||||
Keytab: kerberosKeytab,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -109,18 +142,18 @@ func tryCredsFromLoot(con *console.SliverConsoleClient) (string, string, []byte)
|
||||
prompt := &survey.Confirm{Message: "No credentials provided, use from loot?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if confirm {
|
||||
cred, err := loot.SelectCredentials(con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Invalid loot data, will try to use the SSH agent")
|
||||
} else {
|
||||
switch cred.CredentialType {
|
||||
case clientpb.CredentialType_API_KEY:
|
||||
privKey = []byte(cred.Credential.APIKey)
|
||||
case clientpb.CredentialType_USER_PASSWORD:
|
||||
username = cred.Credential.User
|
||||
password = cred.Credential.Password
|
||||
}
|
||||
}
|
||||
// cred, err := loot.SelectCredentials(con)
|
||||
// if err != nil {
|
||||
// con.PrintErrorf("Invalid loot data, will try to use the SSH agent")
|
||||
// } else {
|
||||
// switch cred.CredentialType {
|
||||
// case clientpb.CredentialType_API_KEY:
|
||||
// privKey = []byte(cred.Credential.APIKey)
|
||||
// case clientpb.CredentialType_USER_PASSWORD:
|
||||
// username = cred.Credential.User
|
||||
// password = cred.Credential.Password
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return username, password, privKey
|
||||
}
|
||||
@@ -85,7 +85,6 @@ func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.Sl
|
||||
)
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
lootName := ctx.Flags.String("name")
|
||||
userLootType := ctx.Flags.String("type")
|
||||
userLootFileType := ctx.Flags.String("file-type")
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", download.Response.Err)
|
||||
@@ -100,17 +99,9 @@ func PrintCat(download *sliverpb.Download, ctx *grumble.Context, con *console.Sl
|
||||
}
|
||||
|
||||
if saveLoot {
|
||||
lootType, err := loot.ValidateLootType(userLootType)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
// Even if the loot type is bad, we can still print the result to the screen
|
||||
// We will not loot it though
|
||||
lootDownload = false
|
||||
}
|
||||
fileType := loot.ValidateLootFileType(userLootFileType, download.Data)
|
||||
|
||||
if lootDownload {
|
||||
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
|
||||
loot.LootDownload(download, lootName, fileType, ctx, con)
|
||||
con.Printf("\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,14 +130,9 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
|
||||
|
||||
if saveLoot {
|
||||
lootName := ctx.Flags.String("name")
|
||||
lootType, err := loot.ValidateLootType(ctx.Flags.String("type"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
// Hand off to the loot package to take care of looting
|
||||
fileType := loot.ValidateLootFileType(ctx.Flags.String("file-type"), download.Data)
|
||||
loot.LootDownload(download, lootName, lootType, fileType, ctx, con)
|
||||
loot.LootDownload(download, lootName, fileType, ctx, con)
|
||||
} else {
|
||||
fileName := filepath.Base(remotePath)
|
||||
dst, err := filepath.Abs(localPath)
|
||||
|
||||
@@ -31,7 +31,11 @@ func GenerateBeaconCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
compile(config, save, con)
|
||||
if !ctx.Flags.Bool("external-builder") {
|
||||
compile(config, save, con)
|
||||
} else {
|
||||
externalBuild(config, save, con)
|
||||
}
|
||||
}
|
||||
|
||||
func parseBeaconFlags(ctx *grumble.Context, con *console.SliverConsoleClient, config *clientpb.ImplantConfig) error {
|
||||
|
||||
@@ -1,10 +1,28 @@
|
||||
package generate
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@@ -51,12 +69,12 @@ func GenerateStagerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
arch := ctx.Flags.String("arch")
|
||||
proto := ctx.Flags.String("protocol")
|
||||
format := ctx.Flags.String("format")
|
||||
badchars := ctx.Flags.String("badchars")
|
||||
badChars := ctx.Flags.String("badchars")
|
||||
save := ctx.Flags.String("save")
|
||||
|
||||
bChars := make([]string, 0)
|
||||
if len(badchars) > 0 {
|
||||
for _, b := range strings.Split(badchars, " ") {
|
||||
if len(badChars) > 0 {
|
||||
for _, b := range strings.Split(badChars, " ") {
|
||||
bChars = append(bChars, fmt.Sprintf("\\x%s", b))
|
||||
}
|
||||
}
|
||||
@@ -98,7 +116,7 @@ func GenerateStagerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(saveTo, stageFile.GetFile().GetData(), 0700)
|
||||
err = os.WriteFile(saveTo, stageFile.GetFile().GetData(), 0700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return
|
||||
|
||||
@@ -22,12 +22,11 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -36,6 +35,8 @@ import (
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
"github.com/bishopfox/sliver/client/spin"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
@@ -82,6 +83,9 @@ var (
|
||||
"windows/386": true,
|
||||
"windows/amd64": true,
|
||||
}
|
||||
|
||||
ErrNoExternalBuilder = errors.New("no external builders are available")
|
||||
ErrNoValidBuilders = errors.New("no valid external builders for target")
|
||||
)
|
||||
|
||||
// GenerateCmd - The main command used to generate implant binaries
|
||||
@@ -94,7 +98,24 @@ func GenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if save == "" {
|
||||
save, _ = os.Getwd()
|
||||
}
|
||||
compile(config, save, con)
|
||||
if !ctx.Flags.Bool("external-builder") {
|
||||
compile(config, save, con)
|
||||
} else {
|
||||
_, err := externalBuild(config, save, con)
|
||||
if err != nil {
|
||||
if err == ErrNoExternalBuilder {
|
||||
con.PrintErrorf("There are no external builders currently connected to the server\n")
|
||||
con.PrintErrorf("See 'builders' command for more information\n")
|
||||
} else if err == ErrNoValidBuilders {
|
||||
con.PrintErrorf("There are external builders connected to the server, but none can build the target you specified\n")
|
||||
con.PrintErrorf("Invalid target %s\n", fmt.Sprintf("%s:%s/%s", config.Format, config.GOOS, config.GOARCH))
|
||||
con.PrintErrorf("See 'builders' command for more information\n")
|
||||
} else {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func expandPath(path string) string {
|
||||
@@ -118,7 +139,7 @@ func saveLocation(save, DefaultName string) (string, error) {
|
||||
if strings.HasSuffix(save, "/") {
|
||||
log.Printf("%s is dir\n", save)
|
||||
os.MkdirAll(save, 0700)
|
||||
saveTo, _ = filepath.Abs(path.Join(saveTo, DefaultName))
|
||||
saveTo, _ = filepath.Abs(filepath.Join(saveTo, DefaultName))
|
||||
} else {
|
||||
log.Printf("%s is not dir\n", save)
|
||||
saveDir := filepath.Dir(save)
|
||||
@@ -132,7 +153,7 @@ func saveLocation(save, DefaultName string) (string, error) {
|
||||
log.Printf("%s does exist\n", save)
|
||||
if fi.IsDir() {
|
||||
log.Printf("%s is dir\n", save)
|
||||
saveTo, _ = filepath.Abs(path.Join(save, DefaultName))
|
||||
saveTo, _ = filepath.Abs(filepath.Join(save, DefaultName))
|
||||
} else {
|
||||
log.Printf("%s is not dir\n", save)
|
||||
prompt := &survey.Confirm{Message: "Overwrite existing file?"}
|
||||
@@ -157,8 +178,9 @@ func nameOfOutputFormat(value clientpb.OutputFormat) string {
|
||||
return "Shared Library"
|
||||
case clientpb.OutputFormat_SHELLCODE:
|
||||
return "Shellcode"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
panic(fmt.Sprintf("Unknown format %v", value))
|
||||
}
|
||||
|
||||
// Shared function that extracts the compile flags from the grumble context
|
||||
@@ -323,6 +345,7 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
ObfuscateSymbols: symbolObfuscation,
|
||||
C2: c2s,
|
||||
CanaryDomains: canaryDomains,
|
||||
TemplateName: ctx.Flags.String("template"),
|
||||
|
||||
WGPeerTunIP: tunIP.String(),
|
||||
WGKeyExchangePort: uint32(ctx.Flags.Int("key-exchange")),
|
||||
@@ -608,6 +631,130 @@ func ParseTCPPivotc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
return c2s, nil
|
||||
}
|
||||
|
||||
func externalBuild(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
|
||||
|
||||
potentialBuilders, err := findExternalBuilders(config, con)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var externalBuilder *clientpb.Builder
|
||||
if len(potentialBuilders) == 1 {
|
||||
externalBuilder = potentialBuilders[0]
|
||||
} else {
|
||||
con.PrintInfof("Found %d external builders that can compile this configuration", len(potentialBuilders))
|
||||
externalBuilder, err = selectExternalBuilder(potentialBuilders, con)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
con.PrintInfof("Using external builder: %s\n", externalBuilder.Name)
|
||||
|
||||
if config.IsBeacon {
|
||||
interval := time.Duration(config.BeaconInterval)
|
||||
con.PrintInfof("Externally generating new %s/%s beacon implant binary (%v)\n", config.GOOS, config.GOARCH, interval)
|
||||
} else {
|
||||
con.PrintInfof("Externally generating new %s/%s implant binary\n", config.GOOS, config.GOARCH)
|
||||
}
|
||||
if config.ObfuscateSymbols {
|
||||
con.PrintInfof("%sSymbol obfuscation is enabled%s\n", console.Bold, console.Normal)
|
||||
} else if !config.Debug {
|
||||
con.PrintErrorf("Symbol obfuscation is %sdisabled%s\n", console.Bold, console.Normal)
|
||||
}
|
||||
start := time.Now()
|
||||
|
||||
listenerID, listener := con.CreateEventListener()
|
||||
|
||||
waiting := true
|
||||
spinner := spin.New()
|
||||
|
||||
sigint := make(chan os.Signal, 1) // Catch keyboard interrupts
|
||||
signal.Notify(sigint, os.Interrupt)
|
||||
|
||||
con.PrintInfof("Creating external build ... ")
|
||||
externalImplantConfig, err := con.Rpc.GenerateExternal(context.Background(), &clientpb.ExternalGenerateReq{
|
||||
Config: config,
|
||||
BuilderName: externalBuilder.Name,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil, err
|
||||
}
|
||||
con.Printf("done\n")
|
||||
|
||||
var name string
|
||||
msgF := "Waiting for external builder to acknowledge build (template: %s) ... %s"
|
||||
for waiting {
|
||||
select {
|
||||
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
elapsed := time.Since(start)
|
||||
msg := fmt.Sprintf(msgF, externalImplantConfig.Config.TemplateName, elapsed.Round(time.Second))
|
||||
fmt.Fprintf(con.App.Stdout(), console.Clearln+" %s %s", spinner.Next(), msg)
|
||||
|
||||
case event := <-listener:
|
||||
switch event.EventType {
|
||||
|
||||
case consts.ExternalBuildFailedEvent:
|
||||
parts := strings.SplitN(string(event.Data), ":", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
if parts[0] == externalImplantConfig.Config.ID {
|
||||
con.RemoveEventListener(listenerID)
|
||||
return nil, fmt.Errorf("external build failed: %s", parts[1])
|
||||
}
|
||||
|
||||
case consts.AcknowledgeBuildEvent:
|
||||
if string(event.Data) == externalImplantConfig.Config.ID {
|
||||
msgF = "External build acknowledged by builder (template: %s) ... %s"
|
||||
}
|
||||
|
||||
case consts.ExternalBuildCompletedEvent:
|
||||
parts := strings.SplitN(string(event.Data), ":", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
if parts[0] == externalImplantConfig.Config.ID {
|
||||
con.RemoveEventListener(listenerID)
|
||||
name = parts[1]
|
||||
waiting = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case <-sigint:
|
||||
waiting = false
|
||||
con.Printf("\n")
|
||||
return nil, fmt.Errorf("user interrupt")
|
||||
}
|
||||
}
|
||||
|
||||
elapsed := time.Since(start)
|
||||
con.PrintInfof("Build completed in %s\n", elapsed.Round(time.Second))
|
||||
|
||||
generated, err := con.Rpc.Regenerate(context.Background(), &clientpb.RegenerateReq{
|
||||
ImplantName: name,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
con.PrintInfof("Build name: %s (%d bytes)\n", name, len(generated.File.Data))
|
||||
|
||||
saveTo, err := saveLocation(save, filepath.Base(generated.File.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = os.WriteFile(saveTo, generated.File.Data, 0700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return nil, err
|
||||
}
|
||||
con.PrintInfof("Implant saved to %s\n", saveTo)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func compile(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) {
|
||||
if config.IsBeacon {
|
||||
interval := time.Duration(config.BeaconInterval)
|
||||
@@ -635,9 +782,8 @@ func compile(config *clientpb.ImplantConfig, save string, con *console.SliverCon
|
||||
return nil, err
|
||||
}
|
||||
|
||||
end := time.Now()
|
||||
elapsed := time.Time{}.Add(end.Sub(start))
|
||||
con.PrintInfof("Build completed in %s\n", elapsed.Format("15:04:05"))
|
||||
elapsed := time.Since(start)
|
||||
con.PrintInfof("Build completed in %s\n", elapsed.Round(time.Second))
|
||||
if len(generated.File.Data) == 0 {
|
||||
con.PrintErrorf("Build failed, no file data\n")
|
||||
return nil, errors.New("no file data")
|
||||
@@ -670,7 +816,7 @@ func compile(config *clientpb.ImplantConfig, save string, con *console.SliverCon
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(saveTo, fileData, 0700)
|
||||
err = os.WriteFile(saveTo, fileData, 0700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return nil, err
|
||||
@@ -760,3 +906,48 @@ func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, tar
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
return confirm
|
||||
}
|
||||
|
||||
func findExternalBuilders(config *clientpb.ImplantConfig, con *console.SliverConsoleClient) ([]*clientpb.Builder, error) {
|
||||
builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(builders.Builders) < 1 {
|
||||
return []*clientpb.Builder{}, ErrNoExternalBuilder
|
||||
}
|
||||
|
||||
validBuilders := []*clientpb.Builder{}
|
||||
for _, builder := range builders.Builders {
|
||||
for _, target := range builder.Targets {
|
||||
if target.GOOS == config.GOOS && target.GOARCH == config.GOARCH && config.Format == target.Format {
|
||||
validBuilders = append(validBuilders, builder)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(validBuilders) < 1 {
|
||||
return []*clientpb.Builder{}, ErrNoValidBuilders
|
||||
}
|
||||
|
||||
return validBuilders, nil
|
||||
}
|
||||
|
||||
func selectExternalBuilder(builders []*clientpb.Builder, con *console.SliverConsoleClient) (*clientpb.Builder, error) {
|
||||
choices := []string{}
|
||||
for _, builder := range builders {
|
||||
choices = append(choices, builder.Name)
|
||||
}
|
||||
choice := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Select an external builder:",
|
||||
Options: choices,
|
||||
}
|
||||
survey.AskOne(prompt, &choice, nil)
|
||||
for _, builder := range builders {
|
||||
if builder.Name == choice {
|
||||
return builder, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrNoValidBuilders
|
||||
}
|
||||
@@ -49,10 +49,10 @@ func ImplantsCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
implantBuildfilters := ImplantBuildFilter{}
|
||||
implantBuildFilters := ImplantBuildFilter{}
|
||||
|
||||
if 0 < len(builds.Configs) {
|
||||
PrintImplantBuilds(builds.Configs, implantBuildfilters, con)
|
||||
PrintImplantBuilds(builds.Configs, implantBuildFilters, con)
|
||||
} else {
|
||||
con.PrintInfof("No implant builds\n")
|
||||
}
|
||||
@@ -65,6 +65,7 @@ func PrintImplantBuilds(configs map[string]*clientpb.ImplantConfig, filters Impl
|
||||
tw.AppendHeader(table.Row{
|
||||
"Name",
|
||||
"Implant Type",
|
||||
"Template",
|
||||
"OS/Arch",
|
||||
"Format",
|
||||
"Command & Control",
|
||||
@@ -103,9 +104,13 @@ func PrintImplantBuilds(configs map[string]*clientpb.ImplantConfig, filters Impl
|
||||
for index, c2 := range config.C2 {
|
||||
c2URLs = append(c2URLs, fmt.Sprintf("[%d] %s", index+1, c2.URL))
|
||||
}
|
||||
if config.TemplateName == "" {
|
||||
config.TemplateName = "sliver"
|
||||
}
|
||||
tw.AppendRow(table.Row{
|
||||
sliverName,
|
||||
implantType,
|
||||
config.TemplateName,
|
||||
fmt.Sprintf("%s/%s", config.GOOS, config.GOARCH),
|
||||
config.Format,
|
||||
strings.Join(c2URLs, "\n"),
|
||||
|
||||
@@ -20,7 +20,6 @@ package generate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -51,7 +50,7 @@ func RegenerateCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(saveTo, regenerate.File.Data, 0700)
|
||||
err = os.WriteFile(saveTo, regenerate.File.Data, 0700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to %s\n", err)
|
||||
return
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/creds"
|
||||
consts "github.com/bishopfox/sliver/client/constants"
|
||||
)
|
||||
|
||||
@@ -95,6 +96,10 @@ var (
|
||||
// Loot
|
||||
consts.LootStr: lootHelp,
|
||||
|
||||
// Creds
|
||||
consts.CredsStr: credsHelp,
|
||||
consts.CredsStr + sep + consts.AddStr: credsAddHelp,
|
||||
consts.CredsStr + sep + consts.AddStr + sep + consts.FileStr: credsAddFileHelp,
|
||||
// Profiles
|
||||
consts.ProfilesStr + sep + consts.NewStr: newProfileHelp,
|
||||
consts.ProfilesStr + sep + consts.GenerateStr: generateProfileHelp,
|
||||
@@ -105,6 +110,9 @@ var (
|
||||
consts.ReactionStr + sep + consts.UnsetStr: reactionUnsetHelp,
|
||||
|
||||
consts.Cursed + sep + consts.CursedChrome: cursedChromeHelp,
|
||||
|
||||
// Builders
|
||||
consts.BuildersStr: buildersHelp,
|
||||
}
|
||||
|
||||
jobsHelp = `[[.Bold]]Command:[[.Normal]] jobs <options>
|
||||
@@ -576,18 +584,11 @@ ssh -l ubuntu ec2-instance ps aux
|
||||
lootHelp = `[[.Bold]]Command:[[.Normal]] loot
|
||||
[[.Bold]]About:[[.Normal]] Store and share loot between operators.
|
||||
|
||||
A piece of loot can be one of two loot types: a file or a credential.
|
||||
A piece of loot is a file, that can be one of two loot types: text or binary.
|
||||
|
||||
[[.Bold]]File Loot[[.Normal]]
|
||||
A file can be binary or text, Sliver will attempt to detect the type of file automatically or you can specify
|
||||
a file type with --file-type. You can add local files as loot using the "local" sub-command, or you can add
|
||||
files from a session using the "remote" sub-command.
|
||||
|
||||
[[.Bold]]Credential Loot[[.Normal]]
|
||||
Credential loot can be a user/password combination, an API key, or a file. You can add user/password and API
|
||||
keys using the "creds" sub-command. To add credential files use either the "local" or "remote" sub-commands
|
||||
with a "--type cred" flag (note the distinction between loot type --type, and file type --file-type). You can
|
||||
additionally specify a --file-type (binary or text) as you would normally.
|
||||
Sliver will attempt to detect the type of file automatically or you can specify a file type with
|
||||
--file-type. You can add local files as loot using the "local" sub-command, or you can add files
|
||||
from a session using the "remote" sub-command.
|
||||
|
||||
[[.Bold]]Examples:[[.Normal]]
|
||||
|
||||
@@ -597,12 +598,6 @@ loot local ./foo.txt
|
||||
# Adding a remote file from the active session:
|
||||
loot remote C:/foo.txt
|
||||
|
||||
# Adding a remote file as a credential from the active session:
|
||||
loot remote --type cred id_rsa
|
||||
|
||||
# Display only credentials:
|
||||
loot --filter creds
|
||||
|
||||
# Display the contents of a piece of loot:
|
||||
loot fetch
|
||||
`
|
||||
@@ -702,6 +697,508 @@ the target extension must have either of these two sets of permissions:
|
||||
|
||||
More information: https://github.com/mandatoryprogrammer/CursedChrome
|
||||
`
|
||||
|
||||
buildersHelp = `[[.Bold]]Command:[[.Normal]] builders
|
||||
[[.Bold]]About:[[.Normal]] Lists external builders currently registered with the server.
|
||||
|
||||
External builders allow the Sliver server offload implant builds onto external machines.
|
||||
For more information: https://github.com/BishopFox/sliver/wiki/External-Builders
|
||||
`
|
||||
|
||||
credsHelp = `[[.Bold]]Command:[[.Normal]] creds
|
||||
[[.Bold]]About:[[.Normal]] Manage credentials database.
|
||||
`
|
||||
|
||||
credsAddHelp = `[[.Bold]]Command:[[.Normal]] creds add
|
||||
[[.Bold]]About:[[.Normal]] Add a credential to the database
|
||||
|
||||
[[.Bold]]Hash Types:[[.Normal]]
|
||||
Sliver uses the same hash identifiers as Hashcat (use the #):
|
||||
|
||||
# | Name | Category
|
||||
======+============================================================+======================================
|
||||
900 | MD4 | Raw Hash
|
||||
0 | MD5 | Raw Hash
|
||||
100 | SHA1 | Raw Hash
|
||||
1300 | SHA2-224 | Raw Hash
|
||||
1400 | SHA2-256 | Raw Hash
|
||||
10800 | SHA2-384 | Raw Hash
|
||||
1700 | SHA2-512 | Raw Hash
|
||||
17300 | SHA3-224 | Raw Hash
|
||||
17400 | SHA3-256 | Raw Hash
|
||||
17500 | SHA3-384 | Raw Hash
|
||||
17600 | SHA3-512 | Raw Hash
|
||||
6000 | RIPEMD-160 | Raw Hash
|
||||
600 | BLAKE2b-512 | Raw Hash
|
||||
11700 | GOST R 34.11-2012 (Streebog) 256-bit, big-endian | Raw Hash
|
||||
11800 | GOST R 34.11-2012 (Streebog) 512-bit, big-endian | Raw Hash
|
||||
6900 | GOST R 34.11-94 | Raw Hash
|
||||
17010 | GPG (AES-128/AES-256 (SHA-1($pass))) | Raw Hash
|
||||
5100 | Half MD5 | Raw Hash
|
||||
17700 | Keccak-224 | Raw Hash
|
||||
17800 | Keccak-256 | Raw Hash
|
||||
17900 | Keccak-384 | Raw Hash
|
||||
18000 | Keccak-512 | Raw Hash
|
||||
6100 | Whirlpool | Raw Hash
|
||||
10100 | SipHash | Raw Hash
|
||||
70 | md5(utf16le($pass)) | Raw Hash
|
||||
170 | sha1(utf16le($pass)) | Raw Hash
|
||||
1470 | sha256(utf16le($pass)) | Raw Hash
|
||||
10870 | sha384(utf16le($pass)) | Raw Hash
|
||||
1770 | sha512(utf16le($pass)) | Raw Hash
|
||||
610 | BLAKE2b-512($pass.$salt) | Raw Hash salted and/or iterated
|
||||
620 | BLAKE2b-512($salt.$pass) | Raw Hash salted and/or iterated
|
||||
10 | md5($pass.$salt) | Raw Hash salted and/or iterated
|
||||
20 | md5($salt.$pass) | Raw Hash salted and/or iterated
|
||||
3800 | md5($salt.$pass.$salt) | Raw Hash salted and/or iterated
|
||||
3710 | md5($salt.md5($pass)) | Raw Hash salted and/or iterated
|
||||
4110 | md5($salt.md5($pass.$salt)) | Raw Hash salted and/or iterated
|
||||
4010 | md5($salt.md5($salt.$pass)) | Raw Hash salted and/or iterated
|
||||
21300 | md5($salt.sha1($salt.$pass)) | Raw Hash salted and/or iterated
|
||||
40 | md5($salt.utf16le($pass)) | Raw Hash salted and/or iterated
|
||||
2600 | md5(md5($pass)) | Raw Hash salted and/or iterated
|
||||
3910 | md5(md5($pass).md5($salt)) | Raw Hash salted and/or iterated
|
||||
3500 | md5(md5(md5($pass))) | Raw Hash salted and/or iterated
|
||||
4400 | md5(sha1($pass)) | Raw Hash salted and/or iterated
|
||||
4410 | md5(sha1($pass).$salt) | Raw Hash salted and/or iterated
|
||||
20900 | md5(sha1($pass).md5($pass).sha1($pass)) | Raw Hash salted and/or iterated
|
||||
21200 | md5(sha1($salt).md5($pass)) | Raw Hash salted and/or iterated
|
||||
4300 | md5(strtoupper(md5($pass))) | Raw Hash salted and/or iterated
|
||||
30 | md5(utf16le($pass).$salt) | Raw Hash salted and/or iterated
|
||||
110 | sha1($pass.$salt) | Raw Hash salted and/or iterated
|
||||
120 | sha1($salt.$pass) | Raw Hash salted and/or iterated
|
||||
4900 | sha1($salt.$pass.$salt) | Raw Hash salted and/or iterated
|
||||
4520 | sha1($salt.sha1($pass)) | Raw Hash salted and/or iterated
|
||||
24300 | sha1($salt.sha1($pass.$salt)) | Raw Hash salted and/or iterated
|
||||
140 | sha1($salt.utf16le($pass)) | Raw Hash salted and/or iterated
|
||||
19300 | sha1($salt1.$pass.$salt2) | Raw Hash salted and/or iterated
|
||||
14400 | sha1(CX) | Raw Hash salted and/or iterated
|
||||
4700 | sha1(md5($pass)) | Raw Hash salted and/or iterated
|
||||
4710 | sha1(md5($pass).$salt) | Raw Hash salted and/or iterated
|
||||
21100 | sha1(md5($pass.$salt)) | Raw Hash salted and/or iterated
|
||||
18500 | sha1(md5(md5($pass))) | Raw Hash salted and/or iterated
|
||||
4500 | sha1(sha1($pass)) | Raw Hash salted and/or iterated
|
||||
4510 | sha1(sha1($pass).$salt) | Raw Hash salted and/or iterated
|
||||
5000 | sha1(sha1($salt.$pass.$salt)) | Raw Hash salted and/or iterated
|
||||
130 | sha1(utf16le($pass).$salt) | Raw Hash salted and/or iterated
|
||||
1410 | sha256($pass.$salt) | Raw Hash salted and/or iterated
|
||||
1420 | sha256($salt.$pass) | Raw Hash salted and/or iterated
|
||||
22300 | sha256($salt.$pass.$salt) | Raw Hash salted and/or iterated
|
||||
20720 | sha256($salt.sha256($pass)) | Raw Hash salted and/or iterated
|
||||
21420 | sha256($salt.sha256_bin($pass)) | Raw Hash salted and/or iterated
|
||||
1440 | sha256($salt.utf16le($pass)) | Raw Hash salted and/or iterated
|
||||
20800 | sha256(md5($pass)) | Raw Hash salted and/or iterated
|
||||
20710 | sha256(sha256($pass).$salt) | Raw Hash salted and/or iterated
|
||||
21400 | sha256(sha256_bin($pass)) | Raw Hash salted and/or iterated
|
||||
1430 | sha256(utf16le($pass).$salt) | Raw Hash salted and/or iterated
|
||||
10810 | sha384($pass.$salt) | Raw Hash salted and/or iterated
|
||||
10820 | sha384($salt.$pass) | Raw Hash salted and/or iterated
|
||||
10840 | sha384($salt.utf16le($pass)) | Raw Hash salted and/or iterated
|
||||
10830 | sha384(utf16le($pass).$salt) | Raw Hash salted and/or iterated
|
||||
1710 | sha512($pass.$salt) | Raw Hash salted and/or iterated
|
||||
1720 | sha512($salt.$pass) | Raw Hash salted and/or iterated
|
||||
1740 | sha512($salt.utf16le($pass)) | Raw Hash salted and/or iterated
|
||||
1730 | sha512(utf16le($pass).$salt) | Raw Hash salted and/or iterated
|
||||
50 | HMAC-MD5 (key = $pass) | Raw Hash authenticated
|
||||
60 | HMAC-MD5 (key = $salt) | Raw Hash authenticated
|
||||
150 | HMAC-SHA1 (key = $pass) | Raw Hash authenticated
|
||||
160 | HMAC-SHA1 (key = $salt) | Raw Hash authenticated
|
||||
1450 | HMAC-SHA256 (key = $pass) | Raw Hash authenticated
|
||||
1460 | HMAC-SHA256 (key = $salt) | Raw Hash authenticated
|
||||
1750 | HMAC-SHA512 (key = $pass) | Raw Hash authenticated
|
||||
1760 | HMAC-SHA512 (key = $salt) | Raw Hash authenticated
|
||||
11750 | HMAC-Streebog-256 (key = $pass), big-endian | Raw Hash authenticated
|
||||
11760 | HMAC-Streebog-256 (key = $salt), big-endian | Raw Hash authenticated
|
||||
11850 | HMAC-Streebog-512 (key = $pass), big-endian | Raw Hash authenticated
|
||||
11860 | HMAC-Streebog-512 (key = $salt), big-endian | Raw Hash authenticated
|
||||
28700 | Amazon AWS4-HMAC-SHA256 | Raw Hash authenticated
|
||||
11500 | CRC32 | Raw Checksum
|
||||
27900 | CRC32C | Raw Checksum
|
||||
28000 | CRC64Jones | Raw Checksum
|
||||
18700 | Java Object hashCode() | Raw Checksum
|
||||
25700 | MurmurHash | Raw Checksum
|
||||
27800 | MurmurHash3 | Raw Checksum
|
||||
14100 | 3DES (PT = $salt, key = $pass) | Raw Cipher, Known-plaintext attack
|
||||
14000 | DES (PT = $salt, key = $pass) | Raw Cipher, Known-plaintext attack
|
||||
26401 | AES-128-ECB NOKDF (PT = $salt, key = $pass) | Raw Cipher, Known-plaintext attack
|
||||
26402 | AES-192-ECB NOKDF (PT = $salt, key = $pass) | Raw Cipher, Known-plaintext attack
|
||||
26403 | AES-256-ECB NOKDF (PT = $salt, key = $pass) | Raw Cipher, Known-plaintext attack
|
||||
15400 | ChaCha20 | Raw Cipher, Known-plaintext attack
|
||||
14500 | Linux Kernel Crypto API (2.4) | Raw Cipher, Known-plaintext attack
|
||||
14900 | Skip32 (PT = $salt, key = $pass) | Raw Cipher, Known-plaintext attack
|
||||
11900 | PBKDF2-HMAC-MD5 | Generic KDF
|
||||
12000 | PBKDF2-HMAC-SHA1 | Generic KDF
|
||||
10900 | PBKDF2-HMAC-SHA256 | Generic KDF
|
||||
12100 | PBKDF2-HMAC-SHA512 | Generic KDF
|
||||
8900 | scrypt | Generic KDF
|
||||
400 | phpass | Generic KDF
|
||||
16100 | TACACS+ | Network Protocol
|
||||
11400 | SIP digest authentication (MD5) | Network Protocol
|
||||
5300 | IKE-PSK MD5 | Network Protocol
|
||||
5400 | IKE-PSK SHA1 | Network Protocol
|
||||
25100 | SNMPv3 HMAC-MD5-96 | Network Protocol
|
||||
25000 | SNMPv3 HMAC-MD5-96/HMAC-SHA1-96 | Network Protocol
|
||||
25200 | SNMPv3 HMAC-SHA1-96 | Network Protocol
|
||||
26700 | SNMPv3 HMAC-SHA224-128 | Network Protocol
|
||||
26800 | SNMPv3 HMAC-SHA256-192 | Network Protocol
|
||||
26900 | SNMPv3 HMAC-SHA384-256 | Network Protocol
|
||||
27300 | SNMPv3 HMAC-SHA512-384 | Network Protocol
|
||||
2500 | WPA-EAPOL-PBKDF2 | Network Protocol
|
||||
2501 | WPA-EAPOL-PMK | Network Protocol
|
||||
22000 | WPA-PBKDF2-PMKID+EAPOL | Network Protocol
|
||||
22001 | WPA-PMK-PMKID+EAPOL | Network Protocol
|
||||
16800 | WPA-PMKID-PBKDF2 | Network Protocol
|
||||
16801 | WPA-PMKID-PMK | Network Protocol
|
||||
7300 | IPMI2 RAKP HMAC-SHA1 | Network Protocol
|
||||
10200 | CRAM-MD5 | Network Protocol
|
||||
16500 | JWT (JSON Web Token) | Network Protocol
|
||||
29200 | Radmin3 | Network Protocol
|
||||
19600 | Kerberos 5, etype 17, TGS-REP | Network Protocol
|
||||
19800 | Kerberos 5, etype 17, Pre-Auth | Network Protocol
|
||||
28800 | Kerberos 5, etype 17, DB | Network Protocol
|
||||
19700 | Kerberos 5, etype 18, TGS-REP | Network Protocol
|
||||
19900 | Kerberos 5, etype 18, Pre-Auth | Network Protocol
|
||||
28900 | Kerberos 5, etype 18, DB | Network Protocol
|
||||
7500 | Kerberos 5, etype 23, AS-REQ Pre-Auth | Network Protocol
|
||||
13100 | Kerberos 5, etype 23, TGS-REP | Network Protocol
|
||||
18200 | Kerberos 5, etype 23, AS-REP | Network Protocol
|
||||
5500 | NetNTLMv1 / NetNTLMv1+ESS | Network Protocol
|
||||
27000 | NetNTLMv1 / NetNTLMv1+ESS (NT) | Network Protocol
|
||||
5600 | NetNTLMv2 | Network Protocol
|
||||
27100 | NetNTLMv2 (NT) | Network Protocol
|
||||
29100 | Flask Session Cookie ($salt.$salt.$pass) | Network Protocol
|
||||
4800 | iSCSI CHAP authentication, MD5(CHAP) | Network Protocol
|
||||
8500 | RACF | Operating System
|
||||
6300 | AIX {smd5} | Operating System
|
||||
6700 | AIX {ssha1} | Operating System
|
||||
6400 | AIX {ssha256} | Operating System
|
||||
6500 | AIX {ssha512} | Operating System
|
||||
3000 | LM | Operating System
|
||||
19000 | QNX /etc/shadow (MD5) | Operating System
|
||||
19100 | QNX /etc/shadow (SHA256) | Operating System
|
||||
19200 | QNX /etc/shadow (SHA512) | Operating System
|
||||
15300 | DPAPI masterkey file v1 (context 1 and 2) | Operating System
|
||||
15310 | DPAPI masterkey file v1 (context 3) | Operating System
|
||||
15900 | DPAPI masterkey file v2 (context 1 and 2) | Operating System
|
||||
15910 | DPAPI masterkey file v2 (context 3) | Operating System
|
||||
7200 | GRUB 2 | Operating System
|
||||
12800 | MS-AzureSync PBKDF2-HMAC-SHA256 | Operating System
|
||||
12400 | BSDi Crypt, Extended DES | Operating System
|
||||
1000 | NTLM | Operating System
|
||||
9900 | Radmin2 | Operating System
|
||||
5800 | Samsung Android Password/PIN | Operating System
|
||||
28100 | Windows Hello PIN/Password | Operating System
|
||||
13800 | Windows Phone 8+ PIN/password | Operating System
|
||||
2410 | Cisco-ASA MD5 | Operating System
|
||||
9200 | Cisco-IOS $8$ (PBKDF2-SHA256) | Operating System
|
||||
9300 | Cisco-IOS $9$ (scrypt) | Operating System
|
||||
5700 | Cisco-IOS type 4 (SHA256) | Operating System
|
||||
2400 | Cisco-PIX MD5 | Operating System
|
||||
8100 | Citrix NetScaler (SHA1) | Operating System
|
||||
22200 | Citrix NetScaler (SHA512) | Operating System
|
||||
1100 | Domain Cached Credentials (DCC), MS Cache | Operating System
|
||||
2100 | Domain Cached Credentials 2 (DCC2), MS Cache 2 | Operating System
|
||||
7000 | FortiGate (FortiOS) | Operating System
|
||||
26300 | FortiGate256 (FortiOS256) | Operating System
|
||||
125 | ArubaOS | Operating System
|
||||
501 | Juniper IVE | Operating System
|
||||
22 | Juniper NetScreen/SSG (ScreenOS) | Operating System
|
||||
15100 | Juniper/NetBSD sha1crypt | Operating System
|
||||
26500 | iPhone passcode (UID key + System Keybag) | Operating System
|
||||
122 | macOS v10.4, macOS v10.5, macOS v10.6 | Operating System
|
||||
1722 | macOS v10.7 | Operating System
|
||||
7100 | macOS v10.8+ (PBKDF2-SHA512) | Operating System
|
||||
3200 | bcrypt $2*$, Blowfish (Unix) | Operating System
|
||||
500 | md5crypt, MD5 (Unix), Cisco-IOS $1$ (MD5) | Operating System
|
||||
1500 | descrypt, DES (Unix), Traditional DES | Operating System
|
||||
29000 | sha1($salt.sha1(utf16le($username).':'.utf16le($pass))) | Operating System
|
||||
7400 | sha256crypt $5$, SHA256 (Unix) | Operating System
|
||||
1800 | sha512crypt $6$, SHA512 (Unix) | Operating System
|
||||
24600 | SQLCipher | Database Server
|
||||
131 | MSSQL (2000) | Database Server
|
||||
132 | MSSQL (2005) | Database Server
|
||||
1731 | MSSQL (2012, 2014) | Database Server
|
||||
24100 | MongoDB ServerKey SCRAM-SHA-1 | Database Server
|
||||
24200 | MongoDB ServerKey SCRAM-SHA-256 | Database Server
|
||||
12 | PostgreSQL | Database Server
|
||||
11100 | PostgreSQL CRAM (MD5) | Database Server
|
||||
28600 | PostgreSQL SCRAM-SHA-256 | Database Server
|
||||
3100 | Oracle H: Type (Oracle 7+) | Database Server
|
||||
112 | Oracle S: Type (Oracle 11+) | Database Server
|
||||
12300 | Oracle T: Type (Oracle 12+) | Database Server
|
||||
7401 | MySQL $A$ (sha256crypt) | Database Server
|
||||
11200 | MySQL CRAM (SHA1) | Database Server
|
||||
200 | MySQL323 | Database Server
|
||||
300 | MySQL4.1/MySQL5 | Database Server
|
||||
8000 | Sybase ASE | Database Server
|
||||
8300 | DNSSEC (NSEC3) | FTP, HTTP, SMTP, LDAP Server
|
||||
25900 | KNX IP Secure - Device Authentication Code | FTP, HTTP, SMTP, LDAP Server
|
||||
16400 | CRAM-MD5 Dovecot | FTP, HTTP, SMTP, LDAP Server
|
||||
1411 | SSHA-256(Base64), LDAP {SSHA256} | FTP, HTTP, SMTP, LDAP Server
|
||||
1711 | SSHA-512(Base64), LDAP {SSHA512} | FTP, HTTP, SMTP, LDAP Server
|
||||
24900 | Dahua Authentication MD5 | FTP, HTTP, SMTP, LDAP Server
|
||||
10901 | RedHat 389-DS LDAP (PBKDF2-HMAC-SHA256) | FTP, HTTP, SMTP, LDAP Server
|
||||
15000 | FileZilla Server >= 0.9.55 | FTP, HTTP, SMTP, LDAP Server
|
||||
12600 | ColdFusion 10+ | FTP, HTTP, SMTP, LDAP Server
|
||||
1600 | Apache $apr1$ MD5, md5apr1, MD5 (APR) | FTP, HTTP, SMTP, LDAP Server
|
||||
141 | Episerver 6.x < .NET 4 | FTP, HTTP, SMTP, LDAP Server
|
||||
1441 | Episerver 6.x >= .NET 4 | FTP, HTTP, SMTP, LDAP Server
|
||||
1421 | hMailServer | FTP, HTTP, SMTP, LDAP Server
|
||||
101 | nsldap, SHA-1(Base64), Netscape LDAP SHA | FTP, HTTP, SMTP, LDAP Server
|
||||
111 | nsldaps, SSHA-1(Base64), Netscape LDAP SSHA | FTP, HTTP, SMTP, LDAP Server
|
||||
7700 | SAP CODVN B (BCODE) | Enterprise Application Software (EAS)
|
||||
7701 | SAP CODVN B (BCODE) from RFC_READ_TABLE | Enterprise Application Software (EAS)
|
||||
7800 | SAP CODVN F/G (PASSCODE) | Enterprise Application Software (EAS)
|
||||
7801 | SAP CODVN F/G (PASSCODE) from RFC_READ_TABLE | Enterprise Application Software (EAS)
|
||||
10300 | SAP CODVN H (PWDSALTEDHASH) iSSHA-1 | Enterprise Application Software (EAS)
|
||||
133 | PeopleSoft | Enterprise Application Software (EAS)
|
||||
13500 | PeopleSoft PS_TOKEN | Enterprise Application Software (EAS)
|
||||
21500 | SolarWinds Orion | Enterprise Application Software (EAS)
|
||||
21501 | SolarWinds Orion v2 | Enterprise Application Software (EAS)
|
||||
24 | SolarWinds Serv-U | Enterprise Application Software (EAS)
|
||||
8600 | Lotus Notes/Domino 5 | Enterprise Application Software (EAS)
|
||||
8700 | Lotus Notes/Domino 6 | Enterprise Application Software (EAS)
|
||||
9100 | Lotus Notes/Domino 8 | Enterprise Application Software (EAS)
|
||||
26200 | OpenEdge Progress Encode | Enterprise Application Software (EAS)
|
||||
20600 | Oracle Transportation Management (SHA256) | Enterprise Application Software (EAS)
|
||||
4711 | Huawei sha1(md5($pass).$salt) | Enterprise Application Software (EAS)
|
||||
20711 | AuthMe sha256 | Enterprise Application Software (EAS)
|
||||
22400 | AES Crypt (SHA256) | Full-Disk Encryption (FDE)
|
||||
27400 | VMware VMX (PBKDF2-HMAC-SHA1 + AES-256-CBC) | Full-Disk Encryption (FDE)
|
||||
14600 | LUKS v1 (legacy) | Full-Disk Encryption (FDE)
|
||||
29541 | LUKS v1 RIPEMD-160 + AES | Full-Disk Encryption (FDE)
|
||||
29542 | LUKS v1 RIPEMD-160 + Serpent | Full-Disk Encryption (FDE)
|
||||
29543 | LUKS v1 RIPEMD-160 + Twofish | Full-Disk Encryption (FDE)
|
||||
29511 | LUKS v1 SHA-1 + AES | Full-Disk Encryption (FDE)
|
||||
29512 | LUKS v1 SHA-1 + Serpent | Full-Disk Encryption (FDE)
|
||||
29513 | LUKS v1 SHA-1 + Twofish | Full-Disk Encryption (FDE)
|
||||
29521 | LUKS v1 SHA-256 + AES | Full-Disk Encryption (FDE)
|
||||
29522 | LUKS v1 SHA-256 + Serpent | Full-Disk Encryption (FDE)
|
||||
29523 | LUKS v1 SHA-256 + Twofish | Full-Disk Encryption (FDE)
|
||||
29531 | LUKS v1 SHA-512 + AES | Full-Disk Encryption (FDE)
|
||||
29532 | LUKS v1 SHA-512 + Serpent | Full-Disk Encryption (FDE)
|
||||
29533 | LUKS v1 SHA-512 + Twofish | Full-Disk Encryption (FDE)
|
||||
13711 | VeraCrypt RIPEMD160 + XTS 512 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13712 | VeraCrypt RIPEMD160 + XTS 1024 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13713 | VeraCrypt RIPEMD160 + XTS 1536 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13741 | VeraCrypt RIPEMD160 + XTS 512 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
13742 | VeraCrypt RIPEMD160 + XTS 1024 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
13743 | VeraCrypt RIPEMD160 + XTS 1536 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
29411 | VeraCrypt RIPEMD160 + XTS 512 bit | Full-Disk Encryption (FDE)
|
||||
29412 | VeraCrypt RIPEMD160 + XTS 1024 bit | Full-Disk Encryption (FDE)
|
||||
29413 | VeraCrypt RIPEMD160 + XTS 1536 bit | Full-Disk Encryption (FDE)
|
||||
29441 | VeraCrypt RIPEMD160 + XTS 512 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
29442 | VeraCrypt RIPEMD160 + XTS 1024 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
29443 | VeraCrypt RIPEMD160 + XTS 1536 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
13751 | VeraCrypt SHA256 + XTS 512 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13752 | VeraCrypt SHA256 + XTS 1024 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13753 | VeraCrypt SHA256 + XTS 1536 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13761 | VeraCrypt SHA256 + XTS 512 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
13762 | VeraCrypt SHA256 + XTS 1024 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
13763 | VeraCrypt SHA256 + XTS 1536 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
29451 | VeraCrypt SHA256 + XTS 512 bit | Full-Disk Encryption (FDE)
|
||||
29452 | VeraCrypt SHA256 + XTS 1024 bit | Full-Disk Encryption (FDE)
|
||||
29453 | VeraCrypt SHA256 + XTS 1536 bit | Full-Disk Encryption (FDE)
|
||||
29461 | VeraCrypt SHA256 + XTS 512 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
29462 | VeraCrypt SHA256 + XTS 1024 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
29463 | VeraCrypt SHA256 + XTS 1536 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
13721 | VeraCrypt SHA512 + XTS 512 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13722 | VeraCrypt SHA512 + XTS 1024 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13723 | VeraCrypt SHA512 + XTS 1536 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
29421 | VeraCrypt SHA512 + XTS 512 bit | Full-Disk Encryption (FDE)
|
||||
29422 | VeraCrypt SHA512 + XTS 1024 bit | Full-Disk Encryption (FDE)
|
||||
29423 | VeraCrypt SHA512 + XTS 1536 bit | Full-Disk Encryption (FDE)
|
||||
13771 | VeraCrypt Streebog-512 + XTS 512 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13772 | VeraCrypt Streebog-512 + XTS 1024 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13773 | VeraCrypt Streebog-512 + XTS 1536 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13781 | VeraCrypt Streebog-512 + XTS 512 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
13782 | VeraCrypt Streebog-512 + XTS 1024 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
13783 | VeraCrypt Streebog-512 + XTS 1536 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
29471 | VeraCrypt Streebog-512 + XTS 512 bit | Full-Disk Encryption (FDE)
|
||||
29472 | VeraCrypt Streebog-512 + XTS 1024 bit | Full-Disk Encryption (FDE)
|
||||
29473 | VeraCrypt Streebog-512 + XTS 1536 bit | Full-Disk Encryption (FDE)
|
||||
29481 | VeraCrypt Streebog-512 + XTS 512 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
29482 | VeraCrypt Streebog-512 + XTS 1024 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
29483 | VeraCrypt Streebog-512 + XTS 1536 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
13731 | VeraCrypt Whirlpool + XTS 512 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13732 | VeraCrypt Whirlpool + XTS 1024 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
13733 | VeraCrypt Whirlpool + XTS 1536 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
29431 | VeraCrypt Whirlpool + XTS 512 bit | Full-Disk Encryption (FDE)
|
||||
29432 | VeraCrypt Whirlpool + XTS 1024 bit | Full-Disk Encryption (FDE)
|
||||
29433 | VeraCrypt Whirlpool + XTS 1536 bit | Full-Disk Encryption (FDE)
|
||||
23900 | BestCrypt v3 Volume Encryption | Full-Disk Encryption (FDE)
|
||||
16700 | FileVault 2 | Full-Disk Encryption (FDE)
|
||||
27500 | VirtualBox (PBKDF2-HMAC-SHA256 & AES-128-XTS) | Full-Disk Encryption (FDE)
|
||||
27600 | VirtualBox (PBKDF2-HMAC-SHA256 & AES-256-XTS) | Full-Disk Encryption (FDE)
|
||||
20011 | DiskCryptor SHA512 + XTS 512 bit | Full-Disk Encryption (FDE)
|
||||
20012 | DiskCryptor SHA512 + XTS 1024 bit | Full-Disk Encryption (FDE)
|
||||
20013 | DiskCryptor SHA512 + XTS 1536 bit | Full-Disk Encryption (FDE)
|
||||
22100 | BitLocker | Full-Disk Encryption (FDE)
|
||||
12900 | Android FDE (Samsung DEK) | Full-Disk Encryption (FDE)
|
||||
8800 | Android FDE <= 4.3 | Full-Disk Encryption (FDE)
|
||||
18300 | Apple File System (APFS) | Full-Disk Encryption (FDE)
|
||||
6211 | TrueCrypt RIPEMD160 + XTS 512 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
6212 | TrueCrypt RIPEMD160 + XTS 1024 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
6213 | TrueCrypt RIPEMD160 + XTS 1536 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
6241 | TrueCrypt RIPEMD160 + XTS 512 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
6242 | TrueCrypt RIPEMD160 + XTS 1024 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
6243 | TrueCrypt RIPEMD160 + XTS 1536 bit + boot-mode (legacy) | Full-Disk Encryption (FDE)
|
||||
29311 | TrueCrypt RIPEMD160 + XTS 512 bit | Full-Disk Encryption (FDE)
|
||||
29312 | TrueCrypt RIPEMD160 + XTS 1024 bit | Full-Disk Encryption (FDE)
|
||||
29313 | TrueCrypt RIPEMD160 + XTS 1536 bit | Full-Disk Encryption (FDE)
|
||||
29341 | TrueCrypt RIPEMD160 + XTS 512 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
29342 | TrueCrypt RIPEMD160 + XTS 1024 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
29343 | TrueCrypt RIPEMD160 + XTS 1536 bit + boot-mode | Full-Disk Encryption (FDE)
|
||||
6221 | TrueCrypt SHA512 + XTS 512 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
6222 | TrueCrypt SHA512 + XTS 1024 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
6223 | TrueCrypt SHA512 + XTS 1536 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
29321 | TrueCrypt SHA512 + XTS 512 bit | Full-Disk Encryption (FDE)
|
||||
29322 | TrueCrypt SHA512 + XTS 1024 bit | Full-Disk Encryption (FDE)
|
||||
29323 | TrueCrypt SHA512 + XTS 1536 bit | Full-Disk Encryption (FDE)
|
||||
6231 | TrueCrypt Whirlpool + XTS 512 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
6232 | TrueCrypt Whirlpool + XTS 1024 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
6233 | TrueCrypt Whirlpool + XTS 1536 bit (legacy) | Full-Disk Encryption (FDE)
|
||||
29331 | TrueCrypt Whirlpool + XTS 512 bit | Full-Disk Encryption (FDE)
|
||||
29332 | TrueCrypt Whirlpool + XTS 1024 bit | Full-Disk Encryption (FDE)
|
||||
29333 | TrueCrypt Whirlpool + XTS 1536 bit | Full-Disk Encryption (FDE)
|
||||
12200 | eCryptfs | Full-Disk Encryption (FDE)
|
||||
10400 | PDF 1.1 - 1.3 (Acrobat 2 - 4) | Document
|
||||
10410 | PDF 1.1 - 1.3 (Acrobat 2 - 4), collider #1 | Document
|
||||
10420 | PDF 1.1 - 1.3 (Acrobat 2 - 4), collider #2 | Document
|
||||
10500 | PDF 1.4 - 1.6 (Acrobat 5 - 8) | Document
|
||||
25400 | PDF 1.4 - 1.6 (Acrobat 5 - 8) - user and owner pass | Document
|
||||
10600 | PDF 1.7 Level 3 (Acrobat 9) | Document
|
||||
10700 | PDF 1.7 Level 8 (Acrobat 10 - 11) | Document
|
||||
9400 | MS Office 2007 | Document
|
||||
9500 | MS Office 2010 | Document
|
||||
9600 | MS Office 2013 | Document
|
||||
25300 | MS Office 2016 - SheetProtection | Document
|
||||
9700 | MS Office <= 2003 $0/$1, MD5 + RC4 | Document
|
||||
9710 | MS Office <= 2003 $0/$1, MD5 + RC4, collider #1 | Document
|
||||
9720 | MS Office <= 2003 $0/$1, MD5 + RC4, collider #2 | Document
|
||||
9810 | MS Office <= 2003 $3, SHA1 + RC4, collider #1 | Document
|
||||
9820 | MS Office <= 2003 $3, SHA1 + RC4, collider #2 | Document
|
||||
9800 | MS Office <= 2003 $3/$4, SHA1 + RC4 | Document
|
||||
18400 | Open Document Format (ODF) 1.2 (SHA-256, AES) | Document
|
||||
18600 | Open Document Format (ODF) 1.1 (SHA-1, Blowfish) | Document
|
||||
16200 | Apple Secure Notes | Document
|
||||
23300 | Apple iWork | Document
|
||||
6600 | 1Password, agilekeychain | Password Manager
|
||||
8200 | 1Password, cloudkeychain | Password Manager
|
||||
9000 | Password Safe v2 | Password Manager
|
||||
5200 | Password Safe v3 | Password Manager
|
||||
6800 | LastPass + LastPass sniffed | Password Manager
|
||||
13400 | KeePass 1 (AES/Twofish) and KeePass 2 (AES) | Password Manager
|
||||
29700 | KeePass 1 (AES/Twofish) and KeePass 2 (AES) - keyfile only | Password Manager
|
||||
23400 | Bitwarden | Password Manager
|
||||
16900 | Ansible Vault | Password Manager
|
||||
26000 | Mozilla key3.db | Password Manager
|
||||
26100 | Mozilla key4.db | Password Manager
|
||||
23100 | Apple Keychain | Password Manager
|
||||
11600 | 7-Zip | Archive
|
||||
12500 | RAR3-hp | Archive
|
||||
23700 | RAR3-p (Uncompressed) | Archive
|
||||
13000 | RAR5 | Archive
|
||||
17220 | PKZIP (Compressed Multi-File) | Archive
|
||||
17200 | PKZIP (Compressed) | Archive
|
||||
17225 | PKZIP (Mixed Multi-File) | Archive
|
||||
17230 | PKZIP (Mixed Multi-File Checksum-Only) | Archive
|
||||
17210 | PKZIP (Uncompressed) | Archive
|
||||
20500 | PKZIP Master Key | Archive
|
||||
20510 | PKZIP Master Key (6 byte optimization) | Archive
|
||||
23001 | SecureZIP AES-128 | Archive
|
||||
23002 | SecureZIP AES-192 | Archive
|
||||
23003 | SecureZIP AES-256 | Archive
|
||||
13600 | WinZip | Archive
|
||||
18900 | Android Backup | Archive
|
||||
24700 | Stuffit5 | Archive
|
||||
13200 | AxCrypt 1 | Archive
|
||||
13300 | AxCrypt 1 in-memory SHA1 | Archive
|
||||
23500 | AxCrypt 2 AES-128 | Archive
|
||||
23600 | AxCrypt 2 AES-256 | Archive
|
||||
14700 | iTunes backup < 10.0 | Archive
|
||||
14800 | iTunes backup >= 10.0 | Archive
|
||||
8400 | WBB3 (Woltlab Burning Board) | Forums, CMS, E-Commerce
|
||||
2612 | PHPS | Forums, CMS, E-Commerce
|
||||
121 | SMF (Simple Machines Forum) > v1.1 | Forums, CMS, E-Commerce
|
||||
3711 | MediaWiki B type | Forums, CMS, E-Commerce
|
||||
4521 | Redmine | Forums, CMS, E-Commerce
|
||||
24800 | Umbraco HMAC-SHA1 | Forums, CMS, E-Commerce
|
||||
11 | Joomla < 2.5.18 | Forums, CMS, E-Commerce
|
||||
13900 | OpenCart | Forums, CMS, E-Commerce
|
||||
11000 | PrestaShop | Forums, CMS, E-Commerce
|
||||
16000 | Tripcode | Forums, CMS, E-Commerce
|
||||
7900 | Drupal7 | Forums, CMS, E-Commerce
|
||||
4522 | PunBB | Forums, CMS, E-Commerce
|
||||
2811 | MyBB 1.2+, IPB2+ (Invision Power Board) | Forums, CMS, E-Commerce
|
||||
2611 | vBulletin < v3.8.5 | Forums, CMS, E-Commerce
|
||||
2711 | vBulletin >= v3.8.5 | Forums, CMS, E-Commerce
|
||||
25600 | bcrypt(md5($pass)) / bcryptmd5 | Forums, CMS, E-Commerce
|
||||
25800 | bcrypt(sha1($pass)) / bcryptsha1 | Forums, CMS, E-Commerce
|
||||
28400 | bcrypt(sha512($pass)) / bcryptsha512 | Forums, CMS, E-Commerce
|
||||
21 | osCommerce, xt:Commerce | Forums, CMS, E-Commerce
|
||||
18100 | TOTP (HMAC-SHA1) | One-Time Password
|
||||
2000 | STDOUT | Plaintext
|
||||
99999 | Plaintext | Plaintext
|
||||
21600 | Web2py pbkdf2-sha512 | Framework
|
||||
10000 | Django (PBKDF2-SHA256) | Framework
|
||||
124 | Django (SHA-1) | Framework
|
||||
12001 | Atlassian (PBKDF2-HMAC-SHA1) | Framework
|
||||
19500 | Ruby on Rails Restful-Authentication | Framework
|
||||
27200 | Ruby on Rails Restful Auth (one round, no sitekey) | Framework
|
||||
30000 | Python Werkzeug MD5 (HMAC-MD5 (key = $salt)) | Framework
|
||||
30120 | Python Werkzeug SHA256 (HMAC-SHA256 (key = $salt)) | Framework
|
||||
20200 | Python passlib pbkdf2-sha512 | Framework
|
||||
20300 | Python passlib pbkdf2-sha256 | Framework
|
||||
20400 | Python passlib pbkdf2-sha1 | Framework
|
||||
24410 | PKCS#8 Private Keys (PBKDF2-HMAC-SHA1 + 3DES/AES) | Private Key
|
||||
24420 | PKCS#8 Private Keys (PBKDF2-HMAC-SHA256 + 3DES/AES) | Private Key
|
||||
15500 | JKS Java Key Store Private Keys (SHA1) | Private Key
|
||||
22911 | RSA/DSA/EC/OpenSSH Private Keys ($0$) | Private Key
|
||||
22921 | RSA/DSA/EC/OpenSSH Private Keys ($6$) | Private Key
|
||||
22931 | RSA/DSA/EC/OpenSSH Private Keys ($1, $3$) | Private Key
|
||||
22941 | RSA/DSA/EC/OpenSSH Private Keys ($4$) | Private Key
|
||||
22951 | RSA/DSA/EC/OpenSSH Private Keys ($5$) | Private Key
|
||||
23200 | XMPP SCRAM PBKDF2-SHA1 | Instant Messaging Service
|
||||
28300 | Teamspeak 3 (channel hash) | Instant Messaging Service
|
||||
22600 | Telegram Desktop < v2.1.14 (PBKDF2-HMAC-SHA1) | Instant Messaging Service
|
||||
24500 | Telegram Desktop >= v2.1.14 (PBKDF2-HMAC-SHA512) | Instant Messaging Service
|
||||
22301 | Telegram Mobile App Passcode (SHA256) | Instant Messaging Service
|
||||
23 | Skype | Instant Messaging Service
|
||||
29600 | Terra Station Wallet (AES256-CBC(PBKDF2($pass))) | Cryptocurrency Wallet
|
||||
26600 | MetaMask Wallet | Cryptocurrency Wallet
|
||||
21000 | BitShares v0.x - sha512(sha512_bin(pass)) | Cryptocurrency Wallet
|
||||
28501 | Bitcoin WIF private key (P2PKH), compressed | Cryptocurrency Wallet
|
||||
28502 | Bitcoin WIF private key (P2PKH), uncompressed | Cryptocurrency Wallet
|
||||
28503 | Bitcoin WIF private key (P2WPKH, Bech32), compressed | Cryptocurrency Wallet
|
||||
28504 | Bitcoin WIF private key (P2WPKH, Bech32), uncompressed | Cryptocurrency Wallet
|
||||
28505 | Bitcoin WIF private key (P2SH(P2WPKH)), compressed | Cryptocurrency Wallet
|
||||
28506 | Bitcoin WIF private key (P2SH(P2WPKH)), uncompressed | Cryptocurrency Wallet
|
||||
11300 | Bitcoin/Litecoin wallet.dat | Cryptocurrency Wallet
|
||||
16600 | Electrum Wallet (Salt-Type 1-3) | Cryptocurrency Wallet
|
||||
21700 | Electrum Wallet (Salt-Type 4) | Cryptocurrency Wallet
|
||||
21800 | Electrum Wallet (Salt-Type 5) | Cryptocurrency Wallet
|
||||
12700 | Blockchain, My Wallet | Cryptocurrency Wallet
|
||||
15200 | Blockchain, My Wallet, V2 | Cryptocurrency Wallet
|
||||
18800 | Blockchain, My Wallet, Second Password (SHA256) | Cryptocurrency Wallet
|
||||
25500 | Stargazer Stellar Wallet XLM | Cryptocurrency Wallet
|
||||
16300 | Ethereum Pre-Sale Wallet, PBKDF2-HMAC-SHA256 | Cryptocurrency Wallet
|
||||
15600 | Ethereum Wallet, PBKDF2-HMAC-SHA256 | Cryptocurrency Wallet
|
||||
15700 | Ethereum Wallet, SCRYPT | Cryptocurrency Wallet
|
||||
22500 | MultiBit Classic .key (MD5) | Cryptocurrency Wallet
|
||||
27700 | MultiBit Classic .wallet (scrypt) | Cryptocurrency Wallet
|
||||
22700 | MultiBit HD (scrypt) | Cryptocurrency Wallet
|
||||
28200 | Exodus Desktop Wallet (scrypt) | Cryptocurrency Wallet
|
||||
`
|
||||
|
||||
credsAddFileHelp = fmt.Sprintf(`[[.Bold]]Command:[[.Normal]] creds add file
|
||||
[[.Bold]]About:[[.Normal]] Add a file containing credentials to the database.
|
||||
|
||||
[[.Bold]]File Formats:[[.Normal]]
|
||||
% 10s - One hash per line.
|
||||
% 10s - A file containing lines of 'username:hash' pairs.
|
||||
% 10s - A CSV file containing 'username,hash' pairs (additional columns ignored).
|
||||
`, creds.HashNewlineFormat, creds.UserColonHashNewlineFormat, creds.CSVFormat)
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -70,7 +70,7 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if aesEncryptKey != "" {
|
||||
// check if aes encryption key is correct length
|
||||
if len(aesEncryptKey)%16 != 0 {
|
||||
con.PrintErrorf("Incorect length of AES Key\n")
|
||||
con.PrintErrorf("Incorrect length of AES Key\n")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
package loot
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// LootAddCredentialCmd - Add a credential type loot
|
||||
func LootAddCredentialCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
prompt := &survey.Select{
|
||||
Message: "Choose a credential type:",
|
||||
Options: []string{
|
||||
clientpb.CredentialType_API_KEY.String(),
|
||||
clientpb.CredentialType_USER_PASSWORD.String(),
|
||||
},
|
||||
}
|
||||
credType := ""
|
||||
survey.AskOne(prompt, &credType, survey.WithValidator(survey.Required))
|
||||
name := ctx.Flags.String("name")
|
||||
if name == "" {
|
||||
namePrompt := &survey.Input{Message: "Credential Name: "}
|
||||
con.Println()
|
||||
survey.AskOne(namePrompt, &name)
|
||||
con.Println()
|
||||
}
|
||||
|
||||
loot := &clientpb.Loot{
|
||||
Type: clientpb.LootType_LOOT_CREDENTIAL,
|
||||
Name: name,
|
||||
Credential: &clientpb.Credential{},
|
||||
}
|
||||
|
||||
switch credType {
|
||||
case clientpb.CredentialType_USER_PASSWORD.String():
|
||||
loot.CredentialType = clientpb.CredentialType_USER_PASSWORD
|
||||
for loot.Credential.User == "" {
|
||||
usernamePrompt := &survey.Input{Message: "Username: "}
|
||||
survey.AskOne(usernamePrompt, &loot.Credential.User)
|
||||
if loot.Credential.User == "" {
|
||||
con.Println("Username is required")
|
||||
}
|
||||
}
|
||||
for loot.Credential.Password == "" {
|
||||
passwordPrompt := &survey.Input{Message: "Password: "}
|
||||
survey.AskOne(passwordPrompt, &loot.Credential.Password)
|
||||
if loot.Credential.Password == "" {
|
||||
con.Println("Password is required")
|
||||
}
|
||||
}
|
||||
case clientpb.CredentialType_API_KEY.String():
|
||||
loot.CredentialType = clientpb.CredentialType_API_KEY
|
||||
usernamePrompt := &survey.Input{Message: "API Key: "}
|
||||
survey.AskOne(usernamePrompt, &loot.Credential.APIKey)
|
||||
}
|
||||
|
||||
loot, err := con.Rpc.LootAdd(context.Background(), loot)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Successfully added loot to server (%s)\n", loot.LootID)
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
@@ -33,21 +32,12 @@ func LootFetchCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
loot, err = con.Rpc.LootContent(context.Background(), loot)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle loot based on its type
|
||||
switch loot.Type {
|
||||
case clientpb.LootType_LOOT_FILE:
|
||||
PrintLootFile(con.App.Stdout(), loot)
|
||||
case clientpb.LootType_LOOT_CREDENTIAL:
|
||||
PrintLootCredential(con.App.Stdout(), loot)
|
||||
}
|
||||
|
||||
PrintLootFile(loot, con)
|
||||
if ctx.Flags.String("save") != "" {
|
||||
savedTo, err := saveLootToDisk(ctx, loot)
|
||||
if err != nil {
|
||||
|
||||
@@ -27,12 +27,11 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/rpcpb"
|
||||
@@ -53,12 +52,7 @@ func AddLootFile(rpc rpcpb.SliverRPCClient, name string, fileName string, data [
|
||||
if len(data) < 1 {
|
||||
return ErrNoLootFileData
|
||||
}
|
||||
var lootType clientpb.LootType
|
||||
if isCredential {
|
||||
lootType = clientpb.LootType_LOOT_CREDENTIAL
|
||||
} else {
|
||||
lootType = clientpb.LootType_LOOT_FILE
|
||||
}
|
||||
|
||||
var lootFileType clientpb.FileType
|
||||
if isText(data) || strings.HasSuffix(fileName, ".txt") {
|
||||
lootFileType = clientpb.FileType_TEXT
|
||||
@@ -67,109 +61,27 @@ func AddLootFile(rpc rpcpb.SliverRPCClient, name string, fileName string, data [
|
||||
}
|
||||
loot := &clientpb.Loot{
|
||||
Name: name,
|
||||
Type: lootType,
|
||||
FileType: lootFileType,
|
||||
File: &commonpb.File{
|
||||
Name: path.Base(fileName),
|
||||
Name: filepath.Base(fileName),
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
if lootType == clientpb.LootType_LOOT_CREDENTIAL {
|
||||
loot.CredentialType = clientpb.CredentialType_FILE
|
||||
}
|
||||
|
||||
_, err := rpc.LootAdd(context.Background(), loot)
|
||||
return err
|
||||
}
|
||||
|
||||
// AddLootUserPassword - Add user/password as loot
|
||||
func AddLootUserPassword(rpc rpcpb.SliverRPCClient, name string, user string, password string) error {
|
||||
loot := &clientpb.Loot{
|
||||
Name: name,
|
||||
Type: clientpb.LootType_LOOT_CREDENTIAL,
|
||||
CredentialType: clientpb.CredentialType_USER_PASSWORD,
|
||||
Credential: &clientpb.Credential{
|
||||
User: user,
|
||||
Password: password,
|
||||
},
|
||||
}
|
||||
_, err := rpc.LootAdd(context.Background(), loot)
|
||||
return err
|
||||
}
|
||||
|
||||
// AddLootAPIKey - Add a api key as loot
|
||||
func AddLootAPIKey(rpc rpcpb.SliverRPCClient, name string, apiKey string) error {
|
||||
loot := &clientpb.Loot{
|
||||
Name: name,
|
||||
Type: clientpb.LootType_LOOT_CREDENTIAL,
|
||||
CredentialType: clientpb.CredentialType_API_KEY,
|
||||
Credential: &clientpb.Credential{
|
||||
APIKey: apiKey,
|
||||
},
|
||||
}
|
||||
_, err := rpc.LootAdd(context.Background(), loot)
|
||||
return err
|
||||
}
|
||||
|
||||
// SelectCredentials - An interactive menu for the user to select a piece of loot
|
||||
func SelectCredentials(con *console.SliverConsoleClient) (*clientpb.Loot, error) {
|
||||
allLoot, err := con.Rpc.LootAllOf(context.Background(), &clientpb.Loot{
|
||||
Type: clientpb.LootType_LOOT_CREDENTIAL,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
|
||||
// Render selection table
|
||||
buf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
|
||||
for _, loot := range allLoot.Loot {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t\n", loot.Name, loot.CredentialType, loot.LootID)
|
||||
}
|
||||
table.Flush()
|
||||
options := strings.Split(buf.String(), "\n")
|
||||
options = options[:len(options)-1]
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("no loot to select from")
|
||||
}
|
||||
|
||||
selected := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a piece of credentials:",
|
||||
Options: options,
|
||||
}
|
||||
err = survey.AskOne(prompt, &selected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for index, value := range options {
|
||||
if value == selected {
|
||||
return allLoot.Loot[index], nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("loot not found")
|
||||
}
|
||||
|
||||
// SelectLoot - Interactive menu for the user to select a piece loot (all types)
|
||||
func SelectLoot(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) (*clientpb.Loot, error) {
|
||||
|
||||
// Fetch data with optional filter
|
||||
filter := ctx.Flags.String("filter")
|
||||
var allLoot *clientpb.AllLoot
|
||||
var err error
|
||||
if filter == "" {
|
||||
allLoot, err = rpc.LootAll(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
lootType, err := lootTypeFromHumanStr(filter)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidFileType
|
||||
}
|
||||
allLoot, err = rpc.LootAllOf(context.Background(), &clientpb.Loot{Type: lootType})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allLoot, err = rpc.LootAll(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Render selection table
|
||||
@@ -181,9 +93,9 @@ func SelectLoot(ctx *grumble.Context, rpc rpcpb.SliverRPCClient) (*clientpb.Loot
|
||||
filename = loot.File.Name
|
||||
}
|
||||
if loot.Name == filename {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t\n", loot.Name, loot.Type, loot.LootID)
|
||||
fmt.Fprintf(table, "%s\t%s\t\n", loot.Name, loot.ID)
|
||||
} else {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", loot.Name, filename, loot.Type, loot.LootID)
|
||||
fmt.Fprintf(table, "%s\t(File name: %s)\t%s\t\n", loot.Name, filename, loot.ID)
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
|
||||
@@ -21,9 +21,9 @@ package loot
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
@@ -44,35 +44,14 @@ func LootAddLocalCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
name = path.Base(localPath)
|
||||
}
|
||||
|
||||
var lootType clientpb.LootType
|
||||
var err error
|
||||
lootTypeStr := ctx.Flags.String("type")
|
||||
if lootTypeStr != "" {
|
||||
lootType, err = lootTypeFromHumanStr(lootTypeStr)
|
||||
if err == ErrInvalidLootType {
|
||||
con.PrintErrorf("Invalid loot type %s\n", lootTypeStr)
|
||||
return
|
||||
}
|
||||
var lootFileType clientpb.FileType
|
||||
if isTextFile(localPath) {
|
||||
lootFileType = clientpb.FileType_TEXT
|
||||
} else {
|
||||
lootType = clientpb.LootType_LOOT_FILE
|
||||
lootFileType = clientpb.FileType_BINARY
|
||||
}
|
||||
|
||||
lootFileTypeStr := ctx.Flags.String("file-type")
|
||||
var lootFileType clientpb.FileType
|
||||
if lootFileTypeStr != "" {
|
||||
lootFileType, err = lootFileTypeFromHumanStr(lootFileTypeStr)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if isTextFile(localPath) {
|
||||
lootFileType = clientpb.FileType_TEXT
|
||||
} else {
|
||||
lootFileType = clientpb.FileType_BINARY
|
||||
}
|
||||
}
|
||||
data, err := ioutil.ReadFile(localPath)
|
||||
data, err := os.ReadFile(localPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to read file %s\n", err)
|
||||
return
|
||||
@@ -80,16 +59,12 @@ func LootAddLocalCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
loot := &clientpb.Loot{
|
||||
Name: name,
|
||||
Type: lootType,
|
||||
FileType: lootFileType,
|
||||
File: &commonpb.File{
|
||||
Name: path.Base(localPath),
|
||||
Name: filepath.Base(localPath),
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
if lootType == clientpb.LootType_LOOT_CREDENTIAL {
|
||||
loot.CredentialType = clientpb.CredentialType_FILE
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Uploading loot from %s", localPath), ctrl)
|
||||
@@ -100,5 +75,5 @@ func LootAddLocalCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
|
||||
con.PrintInfof("Successfully added loot to server (%s)\n", loot.LootID)
|
||||
con.PrintInfof("Successfully added loot to server (%s)\n", loot.ID)
|
||||
}
|
||||
+42
-244
@@ -19,113 +19,85 @@ package loot
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/util"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// LootCmd - The loot root command
|
||||
func LootCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
filter := ctx.Flags.String("filter")
|
||||
var allLoot *clientpb.AllLoot
|
||||
var err error
|
||||
if filter == "" {
|
||||
allLoot, err = con.Rpc.LootAll(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to fetch loot %s\n", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
lootType, err := lootTypeFromHumanStr(filter)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Invalid loot type see --help")
|
||||
return
|
||||
}
|
||||
allLoot, err = con.Rpc.LootAllOf(context.Background(), &clientpb.Loot{Type: lootType})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to fetch loot %s\n", err)
|
||||
return
|
||||
}
|
||||
allLoot, err := con.Rpc.LootAll(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to fetch loot %s\n", err)
|
||||
return
|
||||
}
|
||||
if filter == "" {
|
||||
PrintAllLootTable(con.App.Stdout(), allLoot)
|
||||
} else {
|
||||
lootType, _ := lootTypeFromHumanStr(filter)
|
||||
switch lootType {
|
||||
case clientpb.LootType_LOOT_FILE:
|
||||
PrintAllFileLootTable(con.App.Stdout(), allLoot)
|
||||
case clientpb.LootType_LOOT_CREDENTIAL:
|
||||
PrintAllCredentialLootTable(con.App.Stdout(), allLoot)
|
||||
PrintAllFileLootTable(allLoot, con)
|
||||
}
|
||||
|
||||
// PrintAllFileLootTable - Displays a table of all file loot
|
||||
func PrintAllFileLootTable(allLoot *clientpb.AllLoot, con *console.SliverConsoleClient) {
|
||||
if allLoot == nil || len(allLoot.Loot) == 0 {
|
||||
con.PrintInfof("No loot 🙁\n")
|
||||
return
|
||||
}
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Name",
|
||||
"File Name",
|
||||
"Type",
|
||||
"Size",
|
||||
})
|
||||
for _, loot := range allLoot.Loot {
|
||||
if loot.File != nil {
|
||||
tw.AppendRow(table.Row{
|
||||
strings.Split(loot.ID, "-")[0],
|
||||
loot.Name,
|
||||
loot.File.Name,
|
||||
fileTypeToStr(loot.FileType),
|
||||
util.ByteCountBinary(loot.Size),
|
||||
})
|
||||
}
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
|
||||
// PrintLootFile - Display the contents of a piece of loot
|
||||
func PrintLootFile(stdout io.Writer, loot *clientpb.Loot) {
|
||||
func PrintLootFile(loot *clientpb.Loot, con *console.SliverConsoleClient) {
|
||||
if loot.File == nil {
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(stdout)
|
||||
|
||||
if loot.File.Name != "" {
|
||||
fmt.Fprintf(stdout, "%sFile Name:%s %s\n\n", console.Bold, console.Normal, loot.File.Name)
|
||||
con.PrintInfof("%sFile Name:%s %s\n\n", console.Bold, console.Normal, loot.File.Name)
|
||||
}
|
||||
if loot.File.Data != nil && 0 < len(loot.File.Data) {
|
||||
if loot.FileType == clientpb.FileType_TEXT || isText(loot.File.Data) {
|
||||
fmt.Fprintf(stdout, "%s", string(loot.File.Data))
|
||||
con.Printf("%s\n", string(loot.File.Data))
|
||||
} else {
|
||||
fmt.Fprintf(stdout, "<%d bytes of binary data>\n", len(loot.File.Data))
|
||||
con.PrintInfof("<%d bytes of binary data>\n", len(loot.File.Data))
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(stdout, "No file data\n")
|
||||
}
|
||||
}
|
||||
|
||||
func PrintLootCredential(stdout io.Writer, loot *clientpb.Loot) {
|
||||
stdout.Write([]byte("\n"))
|
||||
switch loot.CredentialType {
|
||||
case clientpb.CredentialType_USER_PASSWORD:
|
||||
if loot.Credential != nil {
|
||||
fmt.Fprintf(stdout, "%s User:%s %s\n", console.Bold, console.Normal, loot.Credential.User)
|
||||
fmt.Fprintf(stdout, "%sPassword:%s %s\n", console.Bold, console.Normal, loot.Credential.Password)
|
||||
}
|
||||
if loot.File != nil {
|
||||
PrintLootFile(stdout, loot)
|
||||
}
|
||||
case clientpb.CredentialType_API_KEY:
|
||||
if loot.Credential != nil {
|
||||
fmt.Fprintf(stdout, "%sAPI Key:%s %s\n", console.Bold, console.Normal, loot.Credential.APIKey)
|
||||
}
|
||||
if loot.File != nil {
|
||||
PrintLootFile(stdout, loot)
|
||||
}
|
||||
case clientpb.CredentialType_FILE:
|
||||
if loot.File != nil {
|
||||
PrintLootFile(stdout, loot)
|
||||
}
|
||||
default:
|
||||
fmt.Fprintf(stdout, "%v\n", loot.Credential) // Well, let's give it our best
|
||||
con.PrintInfof("No file data\n")
|
||||
}
|
||||
}
|
||||
|
||||
// Any loot with a "File" can be saved to disk
|
||||
func saveLootToDisk(ctx *grumble.Context, loot *clientpb.Loot) (string, error) {
|
||||
if loot.File == nil {
|
||||
return "", errors.New("Loot does not contain a file")
|
||||
return "", errors.New("loot does not contain a file")
|
||||
}
|
||||
|
||||
saveTo := ctx.Flags.String("save")
|
||||
@@ -144,158 +116,10 @@ func saveLootToDisk(ctx *grumble.Context, loot *clientpb.Loot) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
err = ioutil.WriteFile(saveTo, loot.File.Data, 0600)
|
||||
err = os.WriteFile(saveTo, loot.File.Data, 0600)
|
||||
return saveTo, err
|
||||
}
|
||||
|
||||
// PrintAllLootTable - Displays a table of all loot
|
||||
func PrintAllLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
|
||||
if allLoot == nil || len(allLoot.Loot) == 0 {
|
||||
fmt.Fprintf(stdout, console.Info+"No loot 🙁\n")
|
||||
return
|
||||
}
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Column Headers
|
||||
fmt.Fprintln(table, "Type\tName\tFile Name\tUUID\t")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("Type")),
|
||||
strings.Repeat("=", len("Name")),
|
||||
strings.Repeat("=", len("File Name")),
|
||||
strings.Repeat("=", len("UUID")),
|
||||
)
|
||||
|
||||
for _, loot := range allLoot.Loot {
|
||||
filename := ""
|
||||
if loot.File != nil {
|
||||
filename = loot.File.Name
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t\n", lootTypeToStr(loot.Type), loot.Name, filename, loot.LootID)
|
||||
}
|
||||
|
||||
table.Flush()
|
||||
fmt.Fprintf(stdout, outputBuf.String())
|
||||
}
|
||||
|
||||
// PrintAllFileLootTable - Displays a table of all file loot
|
||||
func PrintAllFileLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
|
||||
if allLoot == nil || len(allLoot.Loot) == 0 {
|
||||
fmt.Fprintf(stdout, console.Info+"No loot 🙁\n")
|
||||
return
|
||||
}
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Column Headers
|
||||
fmt.Fprintln(table, "Type\tName\tFile Name\tSize\tUUID\t")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("Type")),
|
||||
strings.Repeat("=", len("Name")),
|
||||
strings.Repeat("=", len("File Name")),
|
||||
strings.Repeat("=", len("Size")),
|
||||
strings.Repeat("=", len("UUID")),
|
||||
)
|
||||
for _, loot := range allLoot.Loot {
|
||||
if loot.Type != clientpb.LootType_LOOT_FILE {
|
||||
continue
|
||||
}
|
||||
size := 0
|
||||
name := ""
|
||||
if loot.File != nil {
|
||||
name = loot.File.Name
|
||||
size = len(loot.File.Data)
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%d\t%s\t\n",
|
||||
fileTypeToStr(loot.FileType),
|
||||
loot.Name,
|
||||
name,
|
||||
size,
|
||||
loot.LootID,
|
||||
)
|
||||
}
|
||||
|
||||
table.Flush()
|
||||
fmt.Fprintf(stdout, outputBuf.String())
|
||||
}
|
||||
|
||||
// PrintAllCredentialLootTable - Displays a table of all credential loot
|
||||
func PrintAllCredentialLootTable(stdout io.Writer, allLoot *clientpb.AllLoot) {
|
||||
if allLoot == nil || len(allLoot.Loot) == 0 {
|
||||
fmt.Fprintf(stdout, console.Info+"No loot 🙁\n")
|
||||
return
|
||||
}
|
||||
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
|
||||
// Column Headers
|
||||
fmt.Fprintln(table, "Type\tName\tUser\tPassword\tAPI Key\tFile Name\tUUID\t")
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
|
||||
strings.Repeat("=", len("Type")),
|
||||
strings.Repeat("=", len("Name")),
|
||||
strings.Repeat("=", len("User")),
|
||||
strings.Repeat("=", len("Password")),
|
||||
strings.Repeat("=", len("API Key")),
|
||||
strings.Repeat("=", len("File Name")),
|
||||
strings.Repeat("=", len("UUID")),
|
||||
)
|
||||
for _, loot := range allLoot.Loot {
|
||||
if loot.Type != clientpb.LootType_LOOT_CREDENTIAL {
|
||||
continue
|
||||
}
|
||||
fileName := ""
|
||||
if loot.File != nil {
|
||||
fileName = loot.File.Name
|
||||
}
|
||||
user := ""
|
||||
password := ""
|
||||
apiKey := ""
|
||||
if loot.Credential != nil {
|
||||
user = loot.Credential.User
|
||||
password = loot.Credential.Password
|
||||
apiKey = loot.Credential.APIKey
|
||||
}
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
|
||||
credentialTypeToString(loot.CredentialType),
|
||||
loot.Name,
|
||||
user,
|
||||
password,
|
||||
apiKey,
|
||||
fileName,
|
||||
loot.LootID,
|
||||
)
|
||||
}
|
||||
table.Flush()
|
||||
fmt.Fprintf(stdout, outputBuf.String())
|
||||
}
|
||||
|
||||
func lootTypeToStr(value clientpb.LootType) string {
|
||||
switch value {
|
||||
case clientpb.LootType_LOOT_FILE:
|
||||
return "File"
|
||||
case clientpb.LootType_LOOT_CREDENTIAL:
|
||||
return "Credential"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func credentialTypeToString(value clientpb.CredentialType) string {
|
||||
switch value {
|
||||
case clientpb.CredentialType_API_KEY:
|
||||
return "API Key"
|
||||
case clientpb.CredentialType_USER_PASSWORD:
|
||||
return "User/Password"
|
||||
case clientpb.CredentialType_FILE:
|
||||
return "File"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func fileTypeToStr(value clientpb.FileType) string {
|
||||
switch value {
|
||||
case clientpb.FileType_BINARY:
|
||||
@@ -333,32 +157,6 @@ func lootFileTypeFromHumanStr(value string) (clientpb.FileType, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func lootTypeFromHumanStr(value string) (clientpb.LootType, error) {
|
||||
switch strings.ToLower(value) {
|
||||
|
||||
case "c":
|
||||
fallthrough
|
||||
case "cred":
|
||||
fallthrough
|
||||
case "creds":
|
||||
fallthrough
|
||||
case "credentials":
|
||||
fallthrough
|
||||
case "credential":
|
||||
return clientpb.LootType_LOOT_CREDENTIAL, nil
|
||||
|
||||
case "f":
|
||||
fallthrough
|
||||
case "files":
|
||||
fallthrough
|
||||
case "file":
|
||||
return clientpb.LootType_LOOT_FILE, nil
|
||||
|
||||
default:
|
||||
return -1, ErrInvalidLootType
|
||||
}
|
||||
}
|
||||
|
||||
// Taken from: https://cs.opensource.google/go/x/tools/+/refs/tags/v0.1.4:godoc/util/util.go;l=69
|
||||
|
||||
// textExt[x] is true if the extension x indicates a text file, and false otherwise.
|
||||
|
||||
@@ -37,27 +37,6 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func ValidateLootType(lootTypeInput string) (clientpb.LootType, error) {
|
||||
var lootType clientpb.LootType
|
||||
var err error
|
||||
|
||||
if lootTypeInput != "" {
|
||||
lootType, err = lootTypeFromHumanStr(lootTypeInput)
|
||||
if err != nil {
|
||||
/*
|
||||
If we get an error, that means that this loot type was invalid.
|
||||
We will leave it up to the caller to handle the error (output it
|
||||
to the console for example)
|
||||
*/
|
||||
return lootType, fmt.Errorf("Invalid loot type %s", lootTypeInput)
|
||||
}
|
||||
} else {
|
||||
lootType = clientpb.LootType_LOOT_FILE
|
||||
}
|
||||
|
||||
return lootType, err
|
||||
}
|
||||
|
||||
func ValidateLootFileType(lootFileTypeInput string, data []byte) clientpb.FileType {
|
||||
lootFileType, err := lootFileTypeFromHumanStr(lootFileTypeInput)
|
||||
if lootFileType == -1 || err != nil {
|
||||
@@ -72,8 +51,8 @@ func ValidateLootFileType(lootFileTypeInput string, data []byte) clientpb.FileTy
|
||||
}
|
||||
|
||||
/*
|
||||
Eventually this function needs to be refactored out, but we made the decision to
|
||||
duplicate it for now
|
||||
Eventually this function needs to be refactored out, but we made the decision to
|
||||
duplicate it for now
|
||||
*/
|
||||
func PerformDownload(remotePath string, fileName string, ctx *grumble.Context, con *console.SliverConsoleClient) (*sliverpb.Download, error) {
|
||||
ctrl := make(chan bool)
|
||||
@@ -98,39 +77,32 @@ func PerformDownload(remotePath string, fileName string, ctx *grumble.Context, c
|
||||
}
|
||||
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
return nil, fmt.Errorf("%s\n", download.Response.Err)
|
||||
return nil, fmt.Errorf("%s", download.Response.Err)
|
||||
}
|
||||
|
||||
// Decode the downloaded data if required
|
||||
if download.Encoder == "gzip" {
|
||||
download.Data, err = new(encoders.Gzip).Decode(download.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Decoding failed %s", err)
|
||||
return nil, fmt.Errorf("decoding failed %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return download, nil
|
||||
}
|
||||
|
||||
func CreateLootMessage(fileName string, lootName string, lootType clientpb.LootType, lootFileType clientpb.FileType, data []byte) *clientpb.Loot {
|
||||
func CreateLootMessage(fileName string, lootName string, lootFileType clientpb.FileType, data []byte) *clientpb.Loot {
|
||||
if lootName == "" {
|
||||
lootName = fileName
|
||||
}
|
||||
|
||||
lootMessage := &clientpb.Loot{
|
||||
Name: lootName,
|
||||
Type: lootType,
|
||||
FileType: lootFileType,
|
||||
File: &commonpb.File{
|
||||
Name: fileName,
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
|
||||
if lootType == clientpb.LootType_LOOT_CREDENTIAL {
|
||||
lootMessage.CredentialType = clientpb.CredentialType_FILE
|
||||
}
|
||||
|
||||
return lootMessage
|
||||
}
|
||||
|
||||
@@ -146,30 +118,29 @@ func SendLootMessage(loot *clientpb.Loot, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
if loot.Name != loot.File.Name {
|
||||
con.PrintInfof("Successfully looted %s (%s) (ID: %s)\n", loot.File.Name, loot.Name, loot.LootID)
|
||||
con.PrintInfof("Successfully looted %s (%s) (ID: %s)\n", loot.File.Name, loot.Name, loot.ID)
|
||||
} else {
|
||||
con.PrintInfof("Successfully looted %s (ID: %s)\n", loot.Name, loot.LootID)
|
||||
con.PrintInfof("Successfully looted %s (ID: %s)\n", loot.Name, loot.ID)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func LootDownload(download *sliverpb.Download, lootName string, lootType clientpb.LootType, fileType clientpb.FileType, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
func LootDownload(download *sliverpb.Download, lootName string, fileType clientpb.FileType, ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
// Was the download successful?
|
||||
if download.Response != nil && download.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", download.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
/* Construct everything needed to send the loot to the server
|
||||
If this is a directory, we will process each file individually
|
||||
/*
|
||||
Construct everything needed to send the loot to the server
|
||||
If this is a directory, we will process each file individually
|
||||
*/
|
||||
|
||||
// Let's handle the simple case of a file first
|
||||
if !download.IsDir {
|
||||
// filepath.Base does not deal with backslashes correctly in Windows paths, so we have to standardize the path to forward slashes
|
||||
downloadPath := strings.ReplaceAll(download.Path, "\\", "/")
|
||||
lootMessage := CreateLootMessage(filepath.Base(downloadPath), lootName, lootType, fileType, download.Data)
|
||||
lootMessage := CreateLootMessage(filepath.Base(downloadPath), lootName, fileType, download.Data)
|
||||
SendLootMessage(lootMessage, con)
|
||||
} else {
|
||||
// We have to decompress the gzip file first
|
||||
@@ -219,7 +190,7 @@ func LootDownload(download *sliverpb.Download, lootName string, lootType clientp
|
||||
*/
|
||||
fileData, err := io.ReadAll(tarReader)
|
||||
if err == nil {
|
||||
lootMessage := CreateLootMessage(filepath.Base(entryHeader.Name), lootName, lootType, fileType, fileData)
|
||||
lootMessage := CreateLootMessage(filepath.Base(entryHeader.Name), lootName, fileType, fileData)
|
||||
SendLootMessage(lootMessage, con)
|
||||
}
|
||||
}
|
||||
@@ -236,12 +207,6 @@ func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
fileName := filepath.Base(remotePath)
|
||||
name := ctx.Flags.String("name")
|
||||
|
||||
lootType, err := ValidateLootType(ctx.Flags.String("type"))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
download, err := PerformDownload(remotePath, fileName, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
@@ -250,5 +215,5 @@ func LootAddRemoteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
// Determine type based on download buffer
|
||||
lootFileType := ValidateLootFileType(ctx.Flags.String("file-type"), download.Data)
|
||||
LootDownload(download, name, lootType, lootFileType, ctx, con)
|
||||
LootDownload(download, name, lootFileType, ctx, con)
|
||||
}
|
||||
@@ -40,8 +40,8 @@ func LootRenameCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
survey.AskOne(prompt, &newName)
|
||||
|
||||
loot, err = con.Rpc.LootUpdate(context.Background(), &clientpb.Loot{
|
||||
LootID: loot.LootID,
|
||||
Name: newName,
|
||||
ID: loot.ID,
|
||||
Name: newName,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
|
||||
@@ -144,6 +144,6 @@ func LootProcessDump(dump *sliverpb.ProcessDump, lootName string, hostName strin
|
||||
lootName = dumpFileName
|
||||
}
|
||||
|
||||
lootMessage := loot.CreateLootMessage(dumpFileName, lootName, clientpb.LootType_LOOT_FILE, clientpb.FileType_BINARY, dump.GetData())
|
||||
lootMessage := loot.CreateLootMessage(dumpFileName, lootName, clientpb.FileType_BINARY, dump.GetData())
|
||||
loot.SendLootMessage(lootMessage, con)
|
||||
}
|
||||
@@ -122,7 +122,7 @@ func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, ctx *grum
|
||||
var n int
|
||||
n, err = saveToFile.Write(screenshot.Data)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error writting file: %s\n", err)
|
||||
con.PrintErrorf("Error writing file: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ func LootScreenshot(screenshot *sliverpb.Screenshot, lootName string, hostName s
|
||||
lootName = screenshotFileName
|
||||
}
|
||||
|
||||
lootMessage := loot.CreateLootMessage(screenshotFileName, lootName, clientpb.LootType_LOOT_FILE, clientpb.FileType_BINARY, screenshot.GetData())
|
||||
lootMessage := loot.CreateLootMessage(screenshotFileName, lootName, clientpb.FileType_BINARY, screenshot.GetData())
|
||||
loot.SendLootMessage(lootMessage, con)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ package sgn
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
// ShikataGaNaiCmd - Command wrapper for the Shikata Ga Nai shellcode encoder
|
||||
func ShikataGaNaiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
shellcodeFile := ctx.Args.String("shellcode")
|
||||
rawShellcode, err := ioutil.ReadFile(shellcodeFile)
|
||||
rawShellcode, err := os.ReadFile(shellcodeFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to read shellcode file: %s", err)
|
||||
return
|
||||
@@ -72,7 +72,7 @@ func ShikataGaNaiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
outputFile += ".sgn"
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(outputFile, shellcodeResp.Data, 0644)
|
||||
err = os.WriteFile(outputFile, shellcodeResp.Data, 0644)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write shellcode file: %s", err)
|
||||
return
|
||||
|
||||
@@ -215,6 +215,8 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli
|
||||
// ---------------------
|
||||
// Exec commands
|
||||
// ---------------------
|
||||
case sliverpb.MsgInvokeInProcExecuteAssemblyReq:
|
||||
fallthrough
|
||||
case sliverpb.MsgExecuteAssemblyReq:
|
||||
execAssembly := &sliverpb.ExecuteAssembly{}
|
||||
err := proto.Unmarshal(task.Response, execAssembly)
|
||||
|
||||
@@ -44,6 +44,7 @@ import (
|
||||
"github.com/desertbit/go-shlex"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/fatih/color"
|
||||
"github.com/gofrs/uuid"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -91,6 +92,7 @@ type SliverConsoleClient struct {
|
||||
App *grumble.App
|
||||
Rpc rpcpb.SliverRPCClient
|
||||
ActiveTarget *ActiveTarget
|
||||
EventListeners *sync.Map
|
||||
BeaconTaskCallbacks map[string]BeaconTaskCallback
|
||||
BeaconTaskCallbacksMutex *sync.Mutex
|
||||
IsServer bool
|
||||
@@ -120,6 +122,7 @@ func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isS
|
||||
observers: map[int]Observer{},
|
||||
observerID: 0,
|
||||
},
|
||||
EventListeners: &sync.Map{},
|
||||
BeaconTaskCallbacks: map[string]BeaconTaskCallback{},
|
||||
BeaconTaskCallbacksMutex: &sync.Mutex{},
|
||||
IsServer: isServer,
|
||||
@@ -136,7 +139,7 @@ func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isS
|
||||
con.App.SetPrompt(con.GetPrompt())
|
||||
})
|
||||
|
||||
go con.EventLoop()
|
||||
go con.startEventLoop()
|
||||
go core.TunnelLoop(rpc)
|
||||
|
||||
err := con.App.Run()
|
||||
@@ -146,7 +149,7 @@ func Start(rpc rpcpb.SliverRPCClient, bindCmds BindCmds, extraCmds BindCmds, isS
|
||||
return err
|
||||
}
|
||||
|
||||
func (con *SliverConsoleClient) EventLoop() {
|
||||
func (con *SliverConsoleClient) startEventLoop() {
|
||||
eventStream, err := con.Rpc.Events(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
fmt.Printf(Warn+"%s\n", err)
|
||||
@@ -158,7 +161,10 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
return
|
||||
}
|
||||
|
||||
go con.triggerEventListeners(event)
|
||||
|
||||
// Trigger event based on type
|
||||
echoed := false // Only echo the event once
|
||||
switch event.EventType {
|
||||
|
||||
case consts.CanaryEvent:
|
||||
@@ -168,6 +174,7 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
shortID := strings.Split(session.ID, "-")[0]
|
||||
con.PrintEventErrorf(eventMsg+"\n"+Clearln+"\t🔥 Session %s is affected\n", shortID)
|
||||
}
|
||||
echoed = true
|
||||
|
||||
case consts.WatchtowerEvent:
|
||||
msg := string(event.Data)
|
||||
@@ -177,15 +184,19 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
shortID := strings.Split(session.ID, "-")[0]
|
||||
con.PrintEventErrorf(eventMsg+"\n"+Clearln+"\t🔥 Session %s is affected", shortID)
|
||||
}
|
||||
echoed = true
|
||||
|
||||
case consts.JoinedEvent:
|
||||
con.PrintEventInfof("%s has joined the game", event.Client.Operator.Name)
|
||||
echoed = true
|
||||
case consts.LeftEvent:
|
||||
con.PrintEventInfof("%s left the game", event.Client.Operator.Name)
|
||||
echoed = true
|
||||
|
||||
case consts.JobStoppedEvent:
|
||||
job := event.Job
|
||||
con.PrintEventErrorf("Job #%d stopped (%s/%s)", job.ID, job.Protocol, job.Name)
|
||||
echoed = true
|
||||
|
||||
case consts.SessionOpenedEvent:
|
||||
session := event.Session
|
||||
@@ -201,12 +212,14 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
con.PrintEventErrorf("Could not add session to Operator: %s", err)
|
||||
}
|
||||
}
|
||||
echoed = true
|
||||
|
||||
case consts.SessionUpdateEvent:
|
||||
session := event.Session
|
||||
currentTime := time.Now().Format(time.RFC1123)
|
||||
shortID := strings.Split(session.ID, "-")[0]
|
||||
con.PrintEventInfof("Session %s has been updated - %v", shortID, currentTime)
|
||||
echoed = true
|
||||
|
||||
case consts.SessionClosedEvent:
|
||||
session := event.Session
|
||||
@@ -229,6 +242,7 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
}
|
||||
con.PrintEventInfof("Removed session %s from Operator", session.Name)
|
||||
}
|
||||
echoed = true
|
||||
|
||||
case consts.BeaconRegisteredEvent:
|
||||
beacon := &clientpb.Beacon{}
|
||||
@@ -247,18 +261,46 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
con.PrintEventErrorf("Could not add beacon to Operator: %s", err)
|
||||
}
|
||||
}
|
||||
echoed = true
|
||||
|
||||
case consts.BeaconTaskResultEvent:
|
||||
con.triggerBeaconTaskCallback(event.Data)
|
||||
echoed = true
|
||||
|
||||
}
|
||||
|
||||
con.triggerReactions(event)
|
||||
|
||||
con.Printf(Clearln + con.GetPrompt())
|
||||
bufio.NewWriter(con.App.Stdout()).Flush()
|
||||
// Only render if we echoed the event
|
||||
if echoed {
|
||||
con.Printf(Clearln + con.GetPrompt())
|
||||
bufio.NewWriter(con.App.Stdout()).Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (con *SliverConsoleClient) CreateEventListener() (string, <-chan *clientpb.Event) {
|
||||
listener := make(chan *clientpb.Event, 100)
|
||||
listenerID, _ := uuid.NewV4()
|
||||
con.EventListeners.Store(listenerID.String(), listener)
|
||||
return listenerID.String(), listener
|
||||
}
|
||||
|
||||
func (con *SliverConsoleClient) RemoveEventListener(listenerID string) {
|
||||
value, ok := con.EventListeners.LoadAndDelete(listenerID)
|
||||
if ok {
|
||||
close(value.(chan *clientpb.Event))
|
||||
}
|
||||
}
|
||||
|
||||
func (con *SliverConsoleClient) triggerEventListeners(event *clientpb.Event) {
|
||||
con.EventListeners.Range(func(key, value interface{}) bool {
|
||||
listener := value.(chan *clientpb.Event)
|
||||
listener <- event // Do not block while sending the event to the listener
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (con *SliverConsoleClient) triggerReactions(event *clientpb.Event) {
|
||||
reactions := core.Reactions.On(event.EventType)
|
||||
if len(reactions) == 0 {
|
||||
|
||||
@@ -90,7 +90,17 @@ const (
|
||||
BeaconTaskResultEvent = "beacon-taskresult"
|
||||
|
||||
// ExternalBuildEvent
|
||||
ExternalBuildEvent = "external-build"
|
||||
ExternalBuildEvent = "external-build"
|
||||
AcknowledgeBuildEvent = "external-acknowledge"
|
||||
ExternalBuildFailedEvent = "external-build-failed"
|
||||
ExternalBuildCompletedEvent = "external-build-completed"
|
||||
|
||||
// Crackstation Events
|
||||
CrackstationConnected = "crackstation-connected"
|
||||
CrackstationDisconnected = "crackstation-disconnected"
|
||||
|
||||
// Crack Events - Events consumed by crackstations
|
||||
CrackBenchmark = "crack-benchmark"
|
||||
|
||||
// WireGuardNewPeer - New Wireguard peer added
|
||||
WireGuardNewPeer = "wireguard-newpeer"
|
||||
@@ -141,7 +151,8 @@ const (
|
||||
LootLocalStr = "local"
|
||||
LootRemoteStr = "remote"
|
||||
FetchStr = "fetch"
|
||||
LootCredsStr = "creds"
|
||||
CredsStr = "creds"
|
||||
FileStr = "file"
|
||||
|
||||
RenameStr = "rename"
|
||||
|
||||
@@ -253,6 +264,8 @@ const (
|
||||
CursedElectron = "electron"
|
||||
CursedEdge = "edge"
|
||||
CursedCookies = "cookies"
|
||||
|
||||
BuildersStr = "builders"
|
||||
)
|
||||
|
||||
// Groups
|
||||
|
||||
+1
-1
@@ -232,7 +232,7 @@ GEM
|
||||
jekyll-seo-tag (~> 2.1)
|
||||
minitest (5.15.0)
|
||||
multipart-post (2.1.1)
|
||||
nokogiri (1.13.6)
|
||||
nokogiri (1.13.9)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
octokit (4.22.0)
|
||||
|
||||
+3
-3
@@ -20,8 +20,8 @@ set -e
|
||||
|
||||
# Creates the static go asset archives
|
||||
|
||||
GO_VER="1.19.1"
|
||||
GARBLE_VER="1.19.2"
|
||||
GO_VER="1.19.2"
|
||||
GARBLE_VER="1.19.3"
|
||||
SGN_VER="0.0.3"
|
||||
|
||||
GO_ARCH_1="amd64"
|
||||
@@ -156,7 +156,7 @@ echo "-----------------------------------------------------------------"
|
||||
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux"
|
||||
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux"
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux-$GO_ARCH_2"
|
||||
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_linux
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/garble.exe https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_windows.exe"
|
||||
curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/garble.exe https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_windows.exe
|
||||
|
||||
@@ -13,8 +13,8 @@ require (
|
||||
github.com/Ne0nd0g/go-clr v1.0.2
|
||||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/cheggaaa/pb/v3 v3.1.0
|
||||
github.com/chromedp/cdproto v0.0.0-20220827030233-358ed4af73cf
|
||||
github.com/chromedp/chromedp v0.8.5
|
||||
github.com/chromedp/cdproto v0.0.0-20220924210414-0e3390be1777
|
||||
github.com/chromedp/chromedp v0.8.6
|
||||
github.com/desertbit/columnize v2.1.0+incompatible
|
||||
github.com/desertbit/go-shlex v0.1.1
|
||||
github.com/desertbit/grumble v1.1.1
|
||||
@@ -24,37 +24,36 @@ require (
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
||||
github.com/jedib0t/go-pretty/v6 v6.3.8
|
||||
github.com/jedib0t/go-pretty/v6 v6.4.0
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
github.com/lesnuages/snitch v0.6.0
|
||||
github.com/mattn/go-sqlite3 v1.14.15
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.20.2
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/shirou/gopsutil/v3 v3.22.8
|
||||
github.com/shirou/gopsutil/v3 v3.22.9
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/spf13/cobra v1.6.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
github.com/yiya1989/sshkrb5 v0.0.0-20201110125252-a1455b75a35e
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/text v0.4.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220208144051-fde48d68ee68
|
||||
google.golang.org/grpc v1.42.0-dev.0.20211020220737-f00baa6c3c84
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.8
|
||||
gorm.io/driver/mysql v1.3.6
|
||||
gorm.io/driver/postgres v1.3.10
|
||||
gorm.io/driver/sqlite v1.3.6
|
||||
gorm.io/gorm v1.23.9
|
||||
gorm.io/driver/mysql v1.4.3
|
||||
gorm.io/driver/postgres v1.4.5
|
||||
gorm.io/driver/sqlite v1.4.3
|
||||
gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755
|
||||
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5
|
||||
modernc.org/sqlite v1.16.0
|
||||
modernc.org/sqlite v1.19.2
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -71,7 +70,6 @@ require (
|
||||
github.com/demisto/goxforce v0.0.0-20160322194047-db8357535b1d // indirect
|
||||
github.com/desertbit/closer/v3 v3.1.2 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
@@ -80,10 +78,11 @@ require (
|
||||
github.com/gobwas/ws v1.1.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.13.0 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
@@ -92,6 +91,7 @@ require (
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgtype v1.12.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.17.2 // indirect
|
||||
github.com/jcmturner/gofork v1.0.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
@@ -100,34 +100,37 @@ require (
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/slack-go/slack v0.11.3 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/thedevsaddam/gojsonq/v2 v2.5.2 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.org/x/tools v0.1.9 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect
|
||||
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 // indirect
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.1.1 // indirect
|
||||
modernc.org/cc/v3 v3.35.24 // indirect
|
||||
modernc.org/ccgo/v3 v3.15.18 // indirect
|
||||
modernc.org/libc v1.14.12 // indirect
|
||||
modernc.org/mathutil v1.4.1 // indirect
|
||||
modernc.org/memory v1.0.7 // indirect
|
||||
modernc.org/opt v0.1.1 // indirect
|
||||
modernc.org/strutil v1.1.1 // indirect
|
||||
modernc.org/token v1.0.0 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.12 // indirect
|
||||
modernc.org/libc v1.20.3 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.4.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
)
|
||||
@@ -48,10 +48,10 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheggaaa/pb/v3 v3.1.0 h1:3uouEsl32RL7gTiQsuaXD4Bzbfl5tGztXGUvXbs4O04=
|
||||
github.com/cheggaaa/pb/v3 v3.1.0/go.mod h1:YjrevcBqadFDaGQKRdmZxTY42pXEqda48Ea3lt0K/BE=
|
||||
github.com/chromedp/cdproto v0.0.0-20220827030233-358ed4af73cf h1:e0oJZmGJidTRZ0FvWXNhdj9OaOMN30Yf+T3K2Tf3L+s=
|
||||
github.com/chromedp/cdproto v0.0.0-20220827030233-358ed4af73cf/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0=
|
||||
github.com/chromedp/chromedp v0.8.5 h1:HAVg54yQFcn7sg5reVjXtoI1eQaFxhjAjflHACicUFw=
|
||||
github.com/chromedp/chromedp v0.8.5/go.mod h1:xal2XY5Di7m/bzlGwtoYpmgIOfDqCakOIVg5OfdkPZ4=
|
||||
github.com/chromedp/cdproto v0.0.0-20220924210414-0e3390be1777 h1:nEnjcdmVQjhtQm0RFJxRINMw7lsQ8gidtbpsidiDqpY=
|
||||
github.com/chromedp/cdproto v0.0.0-20220924210414-0e3390be1777/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0=
|
||||
github.com/chromedp/chromedp v0.8.6 h1:KobeeqR2dpfKSG1prS3Y6+FbffMmGC6xmAobRXA9QEQ=
|
||||
github.com/chromedp/chromedp v0.8.6/go.mod h1:nBYHoD6YSNzrr82cIeuOzhw1Jo/s2o0QQ+ifTeoCZ+c=
|
||||
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||
@@ -90,7 +90,6 @@ github.com/desertbit/readline v1.5.1/go.mod h1:pHQgTsCFs9Cpfh5mlSUFi9Xa5kkL4d8L1
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
@@ -104,9 +103,6 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 h1:Y5Q2mEwfzjMt5+3u70Gtw93ZOu2UuPeeeTBDntF7FoY=
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@@ -119,7 +115,7 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
@@ -154,12 +150,11 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -167,6 +162,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
@@ -177,13 +174,14 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
@@ -231,8 +229,10 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jedib0t/go-pretty/v6 v6.3.8 h1:p5eZqLFMEGr7CC+9915lC4Dk7Gub6mH7NE35jDhkJsQ=
|
||||
github.com/jedib0t/go-pretty/v6 v6.3.8/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI=
|
||||
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
|
||||
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jedib0t/go-pretty/v6 v6.4.0 h1:YlI/2zYDrweA4MThiYMKtGRfT+2qZOO65ulej8GTcVI=
|
||||
github.com/jedib0t/go-pretty/v6 v6.4.0/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
@@ -286,12 +286,12 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
|
||||
@@ -309,18 +309,6 @@ github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945/go.mod h1:1grVt4Ha
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY=
|
||||
github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -346,8 +334,8 @@ github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OK
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shirou/gopsutil/v3 v3.22.8 h1:a4s3hXogo5mE2PfdfJIonDbstO/P+9JszdfhAHSzD9Y=
|
||||
github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI=
|
||||
github.com/shirou/gopsutil/v3 v3.22.9 h1:yibtJhIVEMcdw+tCTbOPiF1VcsuDeTE4utJ8Dm4c5eA=
|
||||
github.com/shirou/gopsutil/v3 v3.22.9/go.mod h1:bBYl1kjgEJpWpxeHmLI+dVHWtyAwfcmSBLDsp2TNT8A=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
@@ -356,14 +344,17 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||
github.com/slack-go/slack v0.11.3 h1:GN7revxEMax4amCc3El9a+9SGnjmBvSUobs0QnO6ZO8=
|
||||
github.com/slack-go/slack v0.11.3/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
|
||||
github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
|
||||
github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -373,14 +364,17 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/thedevsaddam/gojsonq/v2 v2.5.2 h1:CoMVaYyKFsVj6TjU6APqAhAvC07hTI6IQen8PHzHYY0=
|
||||
github.com/thedevsaddam/gojsonq/v2 v2.5.2/go.mod h1:bv6Xa7kWy82uT0LnXPE2SzGqTj33TAEeR560MdJkiXs=
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43 h1:+sN63SJGqchSsVyNoeW4GyA0Y+g0LvKOHcaeqs1Rf6M=
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43/go.mod h1:dhnDTBbIg31cbJdROP4/Zz6Iw7JPEpiMvOl2LdHSSjE=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/yiya1989/sshkrb5 v0.0.0-20201110125252-a1455b75a35e h1:6SWomM8ezwUwnN3lqKxE4r7bYVwpY9514DQkwBV9IPI=
|
||||
github.com/yiya1989/sshkrb5 v0.0.0-20201110125252-a1455b75a35e/go.mod h1:r1bHcNa22x8rrg/Kj/cDF5bX6UtZrHiQgRFvj3Mnpl8=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
@@ -408,6 +402,7 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
@@ -427,11 +422,10 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@@ -439,7 +433,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
@@ -461,12 +454,11 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||
golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -474,11 +466,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -486,11 +475,9 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -498,9 +485,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -511,8 +496,9 @@ golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
@@ -524,8 +510,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -542,19 +529,16 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d/go.mod h1:5yyfuiqVIJ7t+3MqrpTQ+QqRkMWiESiyDvPNvKYCecg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
||||
@@ -606,173 +590,58 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw=
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=
|
||||
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM=
|
||||
gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI=
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 h1:a9tsXlIDD9SKxotJMK3niV7rPZAJeX2aD/0yg3qlIrg=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU=
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.3.6 h1:BhX1Y/RyALb+T9bZ3t07wLnPZBukt+IRkMn8UZSNbGM=
|
||||
gorm.io/driver/mysql v1.3.6/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
|
||||
gorm.io/driver/postgres v1.3.10 h1:Fsd+pQpFMGlGxxVMUPJhNo8gG8B1lKtk8QQ4/VZZAJw=
|
||||
gorm.io/driver/postgres v1.3.10/go.mod h1:whNfh5WhhHs96honoLjBAMwJGYEuA3m1hvgUbNXhPCw=
|
||||
gorm.io/driver/sqlite v1.3.6 h1:Fi8xNYCUplOqWiPa3/GuCeowRNBRGTf62DEmhMDHeQQ=
|
||||
gorm.io/driver/sqlite v1.3.6/go.mod h1:Sg1/pvnKtbQ7jLXxfZa+jSHvoX8hoZA8cn4xllOMTgE=
|
||||
gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/driver/mysql v1.4.3 h1:/JhWJhO2v17d8hjApTltKNADm7K7YI2ogkR7avJUL3k=
|
||||
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
|
||||
gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc=
|
||||
gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg=
|
||||
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
|
||||
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.9 h1:NSHG021i+MCznokeXR3udGaNyFyBQJW8MbjrJMVCfGw=
|
||||
gorm.io/gorm v1.23.9/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755 h1:7AdrbfcvKnzejfqP5g37fdSZOXH/JvaPIzBIHTOqXKk=
|
||||
gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5 h1:cv/zaNV0nr1mJzaeo4S5mHIm5va1W0/9J3/5prlsuRM=
|
||||
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.24 h1:vlCqjhVwX15t1uwlMPpOpNRC7JTjMZ9lT9DYHKQTFuA=
|
||||
modernc.org/cc/v3 v3.35.24/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
|
||||
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
|
||||
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
|
||||
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
|
||||
modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
|
||||
modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
|
||||
modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
|
||||
modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
|
||||
modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
|
||||
modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
|
||||
modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
|
||||
modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
|
||||
modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
|
||||
modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
|
||||
modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
|
||||
modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
|
||||
modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
|
||||
modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
|
||||
modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
|
||||
modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
|
||||
modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
|
||||
modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
|
||||
modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
|
||||
modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
|
||||
modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
|
||||
modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
|
||||
modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
|
||||
modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
|
||||
modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
|
||||
modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
|
||||
modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
|
||||
modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
|
||||
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
|
||||
modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
|
||||
modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU=
|
||||
modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko=
|
||||
modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA=
|
||||
modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4=
|
||||
modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
|
||||
modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8=
|
||||
modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I=
|
||||
modernc.org/ccgo/v3 v3.15.14/go.mod h1:144Sz2iBCKogb9OKwsu7hQEub3EVgOlyI8wMUPGKUXQ=
|
||||
modernc.org/ccgo/v3 v3.15.15/go.mod h1:z5qltXjU4PJl0pE5nhYQCvA9DhPHiWsl5GWl89+NSYE=
|
||||
modernc.org/ccgo/v3 v3.15.16/go.mod h1:XbKRMeMWMdq712Tr5ECgATYMrzJ+g9zAZEj2ktzBe24=
|
||||
modernc.org/ccgo/v3 v3.15.17/go.mod h1:bofnFkpRFf5gLY+mBZIyTW6FEcp26xi2lgOFk2Rlvs0=
|
||||
modernc.org/ccgo/v3 v3.15.18 h1:X5ym656Ye7/ubL+wox0SeF9aRX5od1UDFn1tAbQR+90=
|
||||
modernc.org/ccgo/v3 v3.15.18/go.mod h1:/2lv3WjHyanEr2sAPdGKRC38n6f0werut9BRXUjjX+A=
|
||||
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.16.12 h1:gWAnL87wSqwM6EQ1a+36O9zMFjqx1FBj0p9rA4xbQCY=
|
||||
modernc.org/ccgo/v3 v3.16.12/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
|
||||
modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
|
||||
modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
|
||||
modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
|
||||
modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
|
||||
modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
|
||||
modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
|
||||
modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
|
||||
modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
|
||||
modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
|
||||
modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
|
||||
modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
|
||||
modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
|
||||
modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
|
||||
modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
|
||||
modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
|
||||
modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
|
||||
modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
|
||||
modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
|
||||
modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
|
||||
modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
|
||||
modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
|
||||
modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
|
||||
modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
|
||||
modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
|
||||
modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
|
||||
modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
|
||||
modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
|
||||
modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
|
||||
modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
|
||||
modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
|
||||
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
|
||||
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
|
||||
modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ=
|
||||
modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
|
||||
modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
|
||||
modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
|
||||
modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
|
||||
modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34=
|
||||
modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ=
|
||||
modernc.org/libc v1.14.6/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak=
|
||||
modernc.org/libc v1.14.7/go.mod h1:f8xfWXW8LW41qb4X5+huVQo5dcfPlq7Cbny2TDheMv0=
|
||||
modernc.org/libc v1.14.8/go.mod h1:9+JCLb1MWSY23smyOpIPbd5ED+rSS/ieiDWUpdyO3mo=
|
||||
modernc.org/libc v1.14.10/go.mod h1:y1MtIWhwpJFpLYm6grAThtuXJKEsY6xkdZmXbRngIdo=
|
||||
modernc.org/libc v1.14.11/go.mod h1:l5/Mz/GrZwOqzwRHA3abgSCnSeJzzTl+Ify0bAwKbAw=
|
||||
modernc.org/libc v1.14.12 h1:pUBZTYoISfbb4pCf4PECENpbvwDBxeKc+/dS9LyOWFM=
|
||||
modernc.org/libc v1.14.12/go.mod h1:fJdoe23MHu2ruPQkFPPqCpToDi5cckzsbmkI6Ez0LqQ=
|
||||
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
|
||||
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
|
||||
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
|
||||
modernc.org/memory v1.0.6/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/memory v1.0.7 h1:UE3cxTRFa5tfUibAV7Jqq8P7zRY0OlJg+yWVIIaluEE=
|
||||
modernc.org/memory v1.0.7/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
|
||||
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.16.0 h1:DdvOGaWN0y+X7t2L7RUD63gcwbVjYZjcBZnA68g44EI=
|
||||
modernc.org/sqlite v1.16.0/go.mod h1:Jwe13ItpESZ+78K5WS6+AjXsUg+JvirsjN3iIDO4C8k=
|
||||
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
|
||||
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||
modernc.org/tcl v1.11.2 h1:mXpsx3AZqJt83uDiFu9UYQVBjNjaWKGCF1YDSlpCL6Y=
|
||||
modernc.org/tcl v1.11.2/go.mod h1:BRzgpajcGdS2qTxniOx9c/dcxjlbA7p12eJNmiriQYo=
|
||||
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
|
||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.3.2 h1:4GWBVMa48UDC7KQ9tnaggN/yTlXg+CdCX9bhgHPQ9AM=
|
||||
modernc.org/z v1.3.2/go.mod h1:PEU2oK2OEA1CfzDTd+8E908qEXhC9s0MfyKp5LZsd+k=
|
||||
modernc.org/libc v1.20.3 h1:BodaDPuUse7taQchAClMmbE/yZp3T2ZBiwCDFyBLEXw=
|
||||
modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
|
||||
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.19.2 h1:1VaNHEe6amuHhelmAOtibvYpAjwLfT4q6cBB2K7ZlQ8=
|
||||
modernc.org/sqlite v1.19.2/go.mod h1:fEgebDYAGTFJj2c/ukKmnaq/0ZQZg0PSYxRa/bHyCDs=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34=
|
||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
|
||||
@@ -15,6 +15,7 @@ require (
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
github.com/yiya1989/sshkrb5 v0.0.0-20201110125252-a1455b75a35e
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
||||
@@ -35,6 +36,8 @@ require (
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||
github.com/jcmturner/gofork v1.0.0 // indirect
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
@@ -44,6 +47,11 @@ require (
|
||||
golang.org/x/tools v0.1.9 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect
|
||||
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 // indirect
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
@@ -42,7 +42,11 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
|
||||
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f h1:5hWo+DzJQSOBl6X+TDac0SPWffRonuRJ2///OYtYRT8=
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f/go.mod h1:f8GY5V3lRzakvEyr49P7hHRYoHtPr8zvj/7JodCoRzw=
|
||||
github.com/lesnuages/go-winio v0.4.19 h1:lFDu1mnhg5em+8zTHO4ChSD11J56xkyE8m3N8IrVmbA=
|
||||
@@ -75,15 +79,19 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43 h1:+sN63SJGqchSsVyNoeW4GyA0Y+g0LvKOHcaeqs1Rf6M=
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43/go.mod h1:dhnDTBbIg31cbJdROP4/Zz6Iw7JPEpiMvOl2LdHSSjE=
|
||||
github.com/yiya1989/sshkrb5 v0.0.0-20201110125252-a1455b75a35e h1:6SWomM8ezwUwnN3lqKxE4r7bYVwpY9514DQkwBV9IPI=
|
||||
github.com/yiya1989/sshkrb5 v0.0.0-20201110125252-a1455b75a35e/go.mod h1:r1bHcNa22x8rrg/Kj/cDF5bX6UtZrHiQgRFvj3Mnpl8=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -162,6 +170,16 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw=
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=
|
||||
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM=
|
||||
gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI=
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 h1:a9tsXlIDD9SKxotJMK3niV7rPZAJeX2aD/0yg3qlIrg=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU=
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -315,7 +315,11 @@ func runSSHCommandHandler(data []byte, resp RPCResponse) {
|
||||
commandReq.Username,
|
||||
commandReq.Password,
|
||||
commandReq.PrivKey,
|
||||
commandReq.Command)
|
||||
commandReq.Krb5Conf,
|
||||
commandReq.Keytab,
|
||||
commandReq.Realm,
|
||||
commandReq.Command,
|
||||
)
|
||||
commandResp := &sliverpb.SSHCommand{
|
||||
Response: &commonpb.Response{},
|
||||
StdOut: stdout,
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package jibberjabber_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJibberJabber(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Jibber Jabber Suite")
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
// +build darwin freebsd linux netbsd openbsd
|
||||
|
||||
package jibberjabber_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
. "github.com/bishopfox/sliver/implant/sliver/locale/jibberjabber"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Unix", func() {
|
||||
AfterEach(func() {
|
||||
os.Setenv("LC_MESSAGES", "")
|
||||
os.Setenv("LC_ALL", "")
|
||||
os.Setenv("LANG", "en_US.UTF-8")
|
||||
})
|
||||
|
||||
Describe("#DetectIETF", func() {
|
||||
Context("Returns IETF encoded locale", func() {
|
||||
It("should return the locale set to LC_MESSAGES", func() {
|
||||
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
|
||||
result, _ := DetectIETF()
|
||||
Ω(result).Should(Equal("fr-FR"))
|
||||
})
|
||||
|
||||
It("should return the locale set to LC_ALL if LC_MESSAGES isn't set", func() {
|
||||
os.Setenv("LC_ALL", "fr_FR.UTF-8")
|
||||
result, _ := DetectIETF()
|
||||
Ω(result).Should(Equal("fr-FR"))
|
||||
})
|
||||
|
||||
It("should return the locale set to LANG if LC_ALL isn't set", func() {
|
||||
os.Setenv("LANG", "fr_FR.UTF-8")
|
||||
result, _ := DetectIETF()
|
||||
Ω(result).Should(Equal("fr-FR"))
|
||||
})
|
||||
|
||||
It("should return an error if it cannot detect a locale", func() {
|
||||
os.Setenv("LANG", "")
|
||||
_, err := DetectIETF()
|
||||
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the locale is simply 'fr'", func() {
|
||||
BeforeEach(func() {
|
||||
os.Setenv("LANG", "fr")
|
||||
})
|
||||
|
||||
It("should return the locale without a territory", func() {
|
||||
language, err := DetectIETF()
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(language).Should(Equal("fr"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectLanguage", func() {
|
||||
Context("Returns encoded language", func() {
|
||||
It("should return the language set to LC_MESSAGES", func() {
|
||||
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguage()
|
||||
Ω(result).Should(Equal("fr"))
|
||||
})
|
||||
|
||||
It("should return the language set to LC_ALL if LC_MESSAGES isn't set", func() {
|
||||
os.Setenv("LC_ALL", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguage()
|
||||
Ω(result).Should(Equal("fr"))
|
||||
})
|
||||
|
||||
It("should return the language set to LANG if LC_ALL isn't set", func() {
|
||||
os.Setenv("LANG", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguage()
|
||||
Ω(result).Should(Equal("fr"))
|
||||
})
|
||||
|
||||
It("should return an error if it cannot detect a language", func() {
|
||||
os.Setenv("LANG", "")
|
||||
_, err := DetectLanguage()
|
||||
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectLanguageTag", func() {
|
||||
Context("Returns encoded language tag", func() {
|
||||
It("should return language tag corresponding to the language set to LC_MESSAGES", func() {
|
||||
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguageTag()
|
||||
base, _ := result.Base()
|
||||
Ω(base.String()).Should(Equal("fr"))
|
||||
region, _ := result.Region()
|
||||
Ω(region.String()).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return language tag corresponding to the language set to LC_ALL if LC_MESSAGES isn't set", func() {
|
||||
os.Setenv("LC_ALL", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguageTag()
|
||||
base, _ := result.Base()
|
||||
Ω(base.String()).Should(Equal("fr"))
|
||||
region, _ := result.Region()
|
||||
Ω(region.String()).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return language tag corresponding to the language set to LANG if LC_ALL isn't set", func() {
|
||||
os.Setenv("LANG", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguageTag()
|
||||
base, _ := result.Base()
|
||||
Ω(base.String()).Should(Equal("fr"))
|
||||
region, _ := result.Region()
|
||||
Ω(region.String()).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return an error if it cannot detect and parse a language tag", func() {
|
||||
os.Setenv("LANG", "")
|
||||
_, err := DetectLanguageTag()
|
||||
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectTerritory", func() {
|
||||
Context("Returns encoded territory", func() {
|
||||
It("should return the territory set to LC_MESSAGES", func() {
|
||||
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
|
||||
result, _ := DetectTerritory()
|
||||
Ω(result).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return the territory set to LC_ALL if LC_MESSAGES isn't set", func() {
|
||||
os.Setenv("LC_ALL", "fr_FR.UTF-8")
|
||||
result, _ := DetectTerritory()
|
||||
Ω(result).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return the territory set to LANG if LC_ALL isn't set", func() {
|
||||
os.Setenv("LANG", "fr_FR.UTF-8")
|
||||
result, _ := DetectTerritory()
|
||||
Ω(result).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return an error if it cannot detect a territory", func() {
|
||||
os.Setenv("LANG", "")
|
||||
_, err := DetectTerritory()
|
||||
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -1,63 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package jibberjabber_test
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
. "github.com/bishopfox/sliver/implant/sliver/locale/jibberjabber"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const (
|
||||
LOCALE_REGEXP = "^[a-z]{2}-[A-Z]{2}$"
|
||||
LANGUAGE_REGEXP = "^[a-z]{2}$"
|
||||
TERRITORY_REGEXP = "^[A-Z]{2}$"
|
||||
)
|
||||
|
||||
var _ = Describe("Windows", func() {
|
||||
BeforeEach(func() {
|
||||
locale, err := DetectIETF()
|
||||
Ω(err).Should(BeNil())
|
||||
Ω(locale).ShouldNot(BeNil())
|
||||
Ω(locale).ShouldNot(Equal(""))
|
||||
})
|
||||
|
||||
Describe("#DetectIETF", func() {
|
||||
It("detects correct IETF locale", func() {
|
||||
locale, _ := DetectIETF()
|
||||
matched, _ := regexp.MatchString(LOCALE_REGEXP, locale)
|
||||
Ω(matched).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectLanguage", func() {
|
||||
It("detects correct Language", func() {
|
||||
language, _ := DetectLanguage()
|
||||
matched, _ := regexp.MatchString(LANGUAGE_REGEXP, language)
|
||||
Ω(matched).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectLanguageTag", func() {
|
||||
It("detects correct Language Tag", func() {
|
||||
result, _ := DetectLanguageTag()
|
||||
base, _ := result.Base()
|
||||
matched, _ := regexp.MatchString(LANGUAGE_REGEXP, base.String())
|
||||
Ω(matched).Should(BeTrue())
|
||||
region, _ := result.Region()
|
||||
matched, _ = regexp.MatchString(TERRITORY_REGEXP, region.String())
|
||||
Ω(matched).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectTerritory", func() {
|
||||
It("detects correct Territory", func() {
|
||||
territory, _ := DetectTerritory()
|
||||
matched, _ := regexp.MatchString(TERRITORY_REGEXP, territory)
|
||||
Ω(matched).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -3,15 +3,21 @@ package ssh
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/yiya1989/sshkrb5/krb5forssh"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
)
|
||||
|
||||
func getClient(host string, port uint16, username string, password string, privKey []byte) (*ssh.Client, error) {
|
||||
func getClient(host string, port uint16, username string, password string, privKey []byte, krb5conf string, keytab []byte, realm string) (*ssh.Client, error) {
|
||||
var authMethods []ssh.AuthMethod
|
||||
if password != "" {
|
||||
// Try password auth first
|
||||
@@ -23,6 +29,17 @@ func getClient(host string, port uint16, username string, password string, privK
|
||||
return nil, err
|
||||
}
|
||||
authMethods = append(authMethods, ssh.PublicKeys(signer))
|
||||
} else if krb5conf != "" && keytab != nil && realm != "" {
|
||||
// Then try kerberos auth
|
||||
krb5ConfData, err := ioutil.ReadFile(krb5conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sshGSSAPIClient, err := krb5forssh.NewKrb5InitiatorClient(string(krb5ConfData), username, realm, keytab)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authMethods = append(authMethods, ssh.GSSAPIWithMICAuthMethod(&sshGSSAPIClient, host))
|
||||
} else {
|
||||
// Use ssh-agent if neither password nor private key has been provided
|
||||
// ssh-agent(1) provides a UNIX socket at $SSH_AUTH_SOCK.
|
||||
@@ -35,6 +52,10 @@ func getClient(host string, port uint16, username string, password string, privK
|
||||
authMethods = append(authMethods, ssh.PublicKeysCallback(agentClient.Signers))
|
||||
}
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Auth methods: %+v\n", authMethods)
|
||||
// {{end}}
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: username,
|
||||
Auth: authMethods,
|
||||
@@ -53,12 +74,12 @@ func getClient(host string, port uint16, username string, password string, privK
|
||||
}
|
||||
|
||||
// RunSSHCommand - SSH to a host and execute a command
|
||||
func RunSSHCommand(host string, port uint16, username string, password string, privKey []byte, command string) (string, string, error) {
|
||||
func RunSSHCommand(host string, port uint16, username string, password string, privKey []byte, krb5conf string, keytab []byte, realm string, command string) (string, string, error) {
|
||||
var (
|
||||
stdout bytes.Buffer
|
||||
stderr bytes.Buffer
|
||||
)
|
||||
sshc, err := getClient(host, port, username, password, privKey)
|
||||
sshc, err := getClient(host, port, username, password, privKey, krb5conf, keytab, realm)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
@@ -211,11 +211,13 @@ func mtlsBeacon(uri *url.URL) *Beacon {
|
||||
return mtls.WriteEnvelope(conn, envelope)
|
||||
},
|
||||
Close: func() error {
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
if conn != nil {
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn = nil
|
||||
}
|
||||
conn = nil
|
||||
return nil
|
||||
},
|
||||
Cleanup: func() error {
|
||||
@@ -341,7 +343,7 @@ func dnsBeacon(uri *url.URL) *Beacon {
|
||||
ActiveC2: uri.String(),
|
||||
Init: func() error {
|
||||
opts := dnsclient.ParseDNSOptions(uri)
|
||||
client, err = dnsclient.DNSStartSession(uri.Host, opts)
|
||||
client, err = dnsclient.DNSStartSession(uri.Hostname(), opts)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[beacon] dns connection error %s", err)
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
language: go
|
||||
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -bench . -benchmem -v ./...
|
||||
+363
@@ -0,0 +1,363 @@
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
# uuid [](https://travis-ci.org/hashicorp/go-uuid)
|
||||
|
||||
Generates UUID-format strings using high quality, _purely random_ bytes. It is **not** intended to be RFC compliant, merely to use a well-understood string representation of a 128-bit value. It can also parse UUID-format strings into their component bytes.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
The full documentation is available on [Godoc](http://godoc.org/github.com/hashicorp/go-uuid).
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// GenerateRandomBytes is used to generate random bytes of given size.
|
||||
func GenerateRandomBytes(size int) ([]byte, error) {
|
||||
return GenerateRandomBytesWithReader(size, rand.Reader)
|
||||
}
|
||||
|
||||
// GenerateRandomBytesWithReader is used to generate random bytes of given size read from a given reader.
|
||||
func GenerateRandomBytesWithReader(size int, reader io.Reader) ([]byte, error) {
|
||||
if reader == nil {
|
||||
return nil, fmt.Errorf("provided reader is nil")
|
||||
}
|
||||
buf := make([]byte, size)
|
||||
if _, err := io.ReadFull(reader, buf); err != nil {
|
||||
return nil, fmt.Errorf("failed to read random bytes: %v", err)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
|
||||
const uuidLen = 16
|
||||
|
||||
// GenerateUUID is used to generate a random UUID
|
||||
func GenerateUUID() (string, error) {
|
||||
return GenerateUUIDWithReader(rand.Reader)
|
||||
}
|
||||
|
||||
// GenerateUUIDWithReader is used to generate a random UUID with a given Reader
|
||||
func GenerateUUIDWithReader(reader io.Reader) (string, error) {
|
||||
if reader == nil {
|
||||
return "", fmt.Errorf("provided reader is nil")
|
||||
}
|
||||
buf, err := GenerateRandomBytesWithReader(uuidLen, reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return FormatUUID(buf)
|
||||
}
|
||||
|
||||
func FormatUUID(buf []byte) (string, error) {
|
||||
if buflen := len(buf); buflen != uuidLen {
|
||||
return "", fmt.Errorf("wrong length byte slice (%d)", buflen)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x",
|
||||
buf[0:4],
|
||||
buf[4:6],
|
||||
buf[6:8],
|
||||
buf[8:10],
|
||||
buf[10:16]), nil
|
||||
}
|
||||
|
||||
func ParseUUID(uuid string) ([]byte, error) {
|
||||
if len(uuid) != 2 * uuidLen + 4 {
|
||||
return nil, fmt.Errorf("uuid string is wrong length")
|
||||
}
|
||||
|
||||
if uuid[8] != '-' ||
|
||||
uuid[13] != '-' ||
|
||||
uuid[18] != '-' ||
|
||||
uuid[23] != '-' {
|
||||
return nil, fmt.Errorf("uuid is improperly formatted")
|
||||
}
|
||||
|
||||
hexStr := uuid[0:8] + uuid[9:13] + uuid[14:18] + uuid[19:23] + uuid[24:36]
|
||||
|
||||
ret, err := hex.DecodeString(hexStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ret) != uuidLen {
|
||||
return nil, fmt.Errorf("decoded hex is the wrong length")
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
Generated
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
This is a temporary repository that will be removed when the issues below are fixed in the core golang code.
|
||||
|
||||
## Issues
|
||||
* [encoding/asn1: cannot marshal into a GeneralString](https://github.com/golang/go/issues/18832)
|
||||
* [encoding/asn1: cannot marshal into slice of strings and pass stringtype parameter tags to members](https://github.com/golang/go/issues/18834)
|
||||
+1003
File diff suppressed because it is too large.
Load diff
+173
@@ -0,0 +1,173 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package asn1
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ASN.1 objects have metadata preceding them:
|
||||
// the tag: the type of the object
|
||||
// a flag denoting if this object is compound or not
|
||||
// the class type: the namespace of the tag
|
||||
// the length of the object, in bytes
|
||||
|
||||
// Here are some standard tags and classes
|
||||
|
||||
// ASN.1 tags represent the type of the following object.
|
||||
const (
|
||||
TagBoolean = 1
|
||||
TagInteger = 2
|
||||
TagBitString = 3
|
||||
TagOctetString = 4
|
||||
TagOID = 6
|
||||
TagEnum = 10
|
||||
TagUTF8String = 12
|
||||
TagSequence = 16
|
||||
TagSet = 17
|
||||
TagPrintableString = 19
|
||||
TagT61String = 20
|
||||
TagIA5String = 22
|
||||
TagUTCTime = 23
|
||||
TagGeneralizedTime = 24
|
||||
TagGeneralString = 27
|
||||
)
|
||||
|
||||
// ASN.1 class types represent the namespace of the tag.
|
||||
const (
|
||||
ClassUniversal = 0
|
||||
ClassApplication = 1
|
||||
ClassContextSpecific = 2
|
||||
ClassPrivate = 3
|
||||
)
|
||||
|
||||
type tagAndLength struct {
|
||||
class, tag, length int
|
||||
isCompound bool
|
||||
}
|
||||
|
||||
// ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead
|
||||
// of" and "in addition to". When not specified, every primitive type has a
|
||||
// default tag in the UNIVERSAL class.
|
||||
//
|
||||
// For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1
|
||||
// doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT
|
||||
// CONTEXT-SPECIFIC 42], that means that the tag is replaced by another.
|
||||
//
|
||||
// On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an
|
||||
// /additional/ tag would wrap the default tag. This explicit tag will have the
|
||||
// compound flag set.
|
||||
//
|
||||
// (This is used in order to remove ambiguity with optional elements.)
|
||||
//
|
||||
// You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we
|
||||
// don't support that here. We support a single layer of EXPLICIT or IMPLICIT
|
||||
// tagging with tag strings on the fields of a structure.
|
||||
|
||||
// fieldParameters is the parsed representation of tag string from a structure field.
|
||||
type fieldParameters struct {
|
||||
optional bool // true iff the field is OPTIONAL
|
||||
explicit bool // true iff an EXPLICIT tag is in use.
|
||||
application bool // true iff an APPLICATION tag is in use.
|
||||
defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
|
||||
tag *int // the EXPLICIT or IMPLICIT tag (maybe nil).
|
||||
stringType int // the string tag to use when marshaling.
|
||||
timeType int // the time tag to use when marshaling.
|
||||
set bool // true iff this should be encoded as a SET
|
||||
omitEmpty bool // true iff this should be omitted if empty when marshaling.
|
||||
|
||||
// Invariants:
|
||||
// if explicit is set, tag is non-nil.
|
||||
}
|
||||
|
||||
// Given a tag string with the format specified in the package comment,
|
||||
// parseFieldParameters will parse it into a fieldParameters structure,
|
||||
// ignoring unknown parts of the string.
|
||||
func parseFieldParameters(str string) (ret fieldParameters) {
|
||||
for _, part := range strings.Split(str, ",") {
|
||||
switch {
|
||||
case part == "optional":
|
||||
ret.optional = true
|
||||
case part == "explicit":
|
||||
ret.explicit = true
|
||||
if ret.tag == nil {
|
||||
ret.tag = new(int)
|
||||
}
|
||||
case part == "generalized":
|
||||
ret.timeType = TagGeneralizedTime
|
||||
case part == "utc":
|
||||
ret.timeType = TagUTCTime
|
||||
case part == "ia5":
|
||||
ret.stringType = TagIA5String
|
||||
// jtasn1 case below added
|
||||
case part == "generalstring":
|
||||
ret.stringType = TagGeneralString
|
||||
case part == "printable":
|
||||
ret.stringType = TagPrintableString
|
||||
case part == "utf8":
|
||||
ret.stringType = TagUTF8String
|
||||
case strings.HasPrefix(part, "default:"):
|
||||
i, err := strconv.ParseInt(part[8:], 10, 64)
|
||||
if err == nil {
|
||||
ret.defaultValue = new(int64)
|
||||
*ret.defaultValue = i
|
||||
}
|
||||
case strings.HasPrefix(part, "tag:"):
|
||||
i, err := strconv.Atoi(part[4:])
|
||||
if err == nil {
|
||||
ret.tag = new(int)
|
||||
*ret.tag = i
|
||||
}
|
||||
case part == "set":
|
||||
ret.set = true
|
||||
case part == "application":
|
||||
ret.application = true
|
||||
if ret.tag == nil {
|
||||
ret.tag = new(int)
|
||||
}
|
||||
case part == "omitempty":
|
||||
ret.omitEmpty = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Given a reflected Go type, getUniversalType returns the default tag number
|
||||
// and expected compound flag.
|
||||
func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) {
|
||||
switch t {
|
||||
case objectIdentifierType:
|
||||
return TagOID, false, true
|
||||
case bitStringType:
|
||||
return TagBitString, false, true
|
||||
case timeType:
|
||||
return TagUTCTime, false, true
|
||||
case enumeratedType:
|
||||
return TagEnum, false, true
|
||||
case bigIntType:
|
||||
return TagInteger, false, true
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return TagBoolean, false, true
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return TagInteger, false, true
|
||||
case reflect.Struct:
|
||||
return TagSequence, true, true
|
||||
case reflect.Slice:
|
||||
if t.Elem().Kind() == reflect.Uint8 {
|
||||
return TagOctetString, false, true
|
||||
}
|
||||
if strings.HasSuffix(t.Name(), "SET") {
|
||||
return TagSet, true, true
|
||||
}
|
||||
return TagSequence, true, true
|
||||
case reflect.String:
|
||||
return TagPrintableString, false, true
|
||||
}
|
||||
return 0, false, false
|
||||
}
|
||||
+659
@@ -0,0 +1,659 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package asn1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// A forkableWriter is an in-memory buffer that can be
|
||||
// 'forked' to create new forkableWriters that bracket the
|
||||
// original. After
|
||||
// pre, post := w.fork()
|
||||
// the overall sequence of bytes represented is logically w+pre+post.
|
||||
type forkableWriter struct {
|
||||
*bytes.Buffer
|
||||
pre, post *forkableWriter
|
||||
}
|
||||
|
||||
func newForkableWriter() *forkableWriter {
|
||||
return &forkableWriter{new(bytes.Buffer), nil, nil}
|
||||
}
|
||||
|
||||
func (f *forkableWriter) fork() (pre, post *forkableWriter) {
|
||||
if f.pre != nil || f.post != nil {
|
||||
panic("have already forked")
|
||||
}
|
||||
f.pre = newForkableWriter()
|
||||
f.post = newForkableWriter()
|
||||
return f.pre, f.post
|
||||
}
|
||||
|
||||
func (f *forkableWriter) Len() (l int) {
|
||||
l += f.Buffer.Len()
|
||||
if f.pre != nil {
|
||||
l += f.pre.Len()
|
||||
}
|
||||
if f.post != nil {
|
||||
l += f.post.Len()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *forkableWriter) writeTo(out io.Writer) (n int, err error) {
|
||||
n, err = out.Write(f.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var nn int
|
||||
|
||||
if f.pre != nil {
|
||||
nn, err = f.pre.writeTo(out)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if f.post != nil {
|
||||
nn, err = f.post.writeTo(out)
|
||||
n += nn
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func marshalBase128Int(out *forkableWriter, n int64) (err error) {
|
||||
if n == 0 {
|
||||
err = out.WriteByte(0)
|
||||
return
|
||||
}
|
||||
|
||||
l := 0
|
||||
for i := n; i > 0; i >>= 7 {
|
||||
l++
|
||||
}
|
||||
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
o := byte(n >> uint(i*7))
|
||||
o &= 0x7f
|
||||
if i != 0 {
|
||||
o |= 0x80
|
||||
}
|
||||
err = out.WriteByte(o)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func marshalInt64(out *forkableWriter, i int64) (err error) {
|
||||
n := int64Length(i)
|
||||
|
||||
for ; n > 0; n-- {
|
||||
err = out.WriteByte(byte(i >> uint((n-1)*8)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func int64Length(i int64) (numBytes int) {
|
||||
numBytes = 1
|
||||
|
||||
for i > 127 {
|
||||
numBytes++
|
||||
i >>= 8
|
||||
}
|
||||
|
||||
for i < -128 {
|
||||
numBytes++
|
||||
i >>= 8
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func marshalBigInt(out *forkableWriter, n *big.Int) (err error) {
|
||||
if n.Sign() < 0 {
|
||||
// A negative number has to be converted to two's-complement
|
||||
// form. So we'll subtract 1 and invert. If the
|
||||
// most-significant-bit isn't set then we'll need to pad the
|
||||
// beginning with 0xff in order to keep the number negative.
|
||||
nMinus1 := new(big.Int).Neg(n)
|
||||
nMinus1.Sub(nMinus1, bigOne)
|
||||
bytes := nMinus1.Bytes()
|
||||
for i := range bytes {
|
||||
bytes[i] ^= 0xff
|
||||
}
|
||||
if len(bytes) == 0 || bytes[0]&0x80 == 0 {
|
||||
err = out.WriteByte(0xff)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
_, err = out.Write(bytes)
|
||||
} else if n.Sign() == 0 {
|
||||
// Zero is written as a single 0 zero rather than no bytes.
|
||||
err = out.WriteByte(0x00)
|
||||
} else {
|
||||
bytes := n.Bytes()
|
||||
if len(bytes) > 0 && bytes[0]&0x80 != 0 {
|
||||
// We'll have to pad this with 0x00 in order to stop it
|
||||
// looking like a negative number.
|
||||
err = out.WriteByte(0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
_, err = out.Write(bytes)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func marshalLength(out *forkableWriter, i int) (err error) {
|
||||
n := lengthLength(i)
|
||||
|
||||
for ; n > 0; n-- {
|
||||
err = out.WriteByte(byte(i >> uint((n-1)*8)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func lengthLength(i int) (numBytes int) {
|
||||
numBytes = 1
|
||||
for i > 255 {
|
||||
numBytes++
|
||||
i >>= 8
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err error) {
|
||||
b := uint8(t.class) << 6
|
||||
if t.isCompound {
|
||||
b |= 0x20
|
||||
}
|
||||
if t.tag >= 31 {
|
||||
b |= 0x1f
|
||||
err = out.WriteByte(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = marshalBase128Int(out, int64(t.tag))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
b |= uint8(t.tag)
|
||||
err = out.WriteByte(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if t.length >= 128 {
|
||||
l := lengthLength(t.length)
|
||||
err = out.WriteByte(0x80 | byte(l))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = marshalLength(out, t.length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = out.WriteByte(byte(t.length))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func marshalBitString(out *forkableWriter, b BitString) (err error) {
|
||||
paddingBits := byte((8 - b.BitLength%8) % 8)
|
||||
err = out.WriteByte(paddingBits)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = out.Write(b.Bytes)
|
||||
return
|
||||
}
|
||||
|
||||
func marshalObjectIdentifier(out *forkableWriter, oid []int) (err error) {
|
||||
if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
|
||||
return StructuralError{"invalid object identifier"}
|
||||
}
|
||||
|
||||
err = marshalBase128Int(out, int64(oid[0]*40+oid[1]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := 2; i < len(oid); i++ {
|
||||
err = marshalBase128Int(out, int64(oid[i]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func marshalPrintableString(out *forkableWriter, s string) (err error) {
|
||||
b := []byte(s)
|
||||
for _, c := range b {
|
||||
if !isPrintable(c) {
|
||||
return StructuralError{"PrintableString contains invalid character"}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = out.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
func marshalIA5String(out *forkableWriter, s string) (err error) {
|
||||
b := []byte(s)
|
||||
for _, c := range b {
|
||||
if c > 127 {
|
||||
return StructuralError{"IA5String contains invalid character"}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = out.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
func marshalUTF8String(out *forkableWriter, s string) (err error) {
|
||||
_, err = out.Write([]byte(s))
|
||||
return
|
||||
}
|
||||
|
||||
func marshalTwoDigits(out *forkableWriter, v int) (err error) {
|
||||
err = out.WriteByte(byte('0' + (v/10)%10))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return out.WriteByte(byte('0' + v%10))
|
||||
}
|
||||
|
||||
func marshalFourDigits(out *forkableWriter, v int) (err error) {
|
||||
var bytes [4]byte
|
||||
for i := range bytes {
|
||||
bytes[3-i] = '0' + byte(v%10)
|
||||
v /= 10
|
||||
}
|
||||
_, err = out.Write(bytes[:])
|
||||
return
|
||||
}
|
||||
|
||||
func outsideUTCRange(t time.Time) bool {
|
||||
year := t.Year()
|
||||
return year < 1950 || year >= 2050
|
||||
}
|
||||
|
||||
func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
|
||||
year := t.Year()
|
||||
|
||||
switch {
|
||||
case 1950 <= year && year < 2000:
|
||||
err = marshalTwoDigits(out, year-1900)
|
||||
case 2000 <= year && year < 2050:
|
||||
err = marshalTwoDigits(out, year-2000)
|
||||
default:
|
||||
return StructuralError{"cannot represent time as UTCTime"}
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return marshalTimeCommon(out, t)
|
||||
}
|
||||
|
||||
func marshalGeneralizedTime(out *forkableWriter, t time.Time) (err error) {
|
||||
year := t.Year()
|
||||
if year < 0 || year > 9999 {
|
||||
return StructuralError{"cannot represent time as GeneralizedTime"}
|
||||
}
|
||||
if err = marshalFourDigits(out, year); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return marshalTimeCommon(out, t)
|
||||
}
|
||||
|
||||
func marshalTimeCommon(out *forkableWriter, t time.Time) (err error) {
|
||||
_, month, day := t.Date()
|
||||
|
||||
err = marshalTwoDigits(out, int(month))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = marshalTwoDigits(out, day)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hour, min, sec := t.Clock()
|
||||
|
||||
err = marshalTwoDigits(out, hour)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = marshalTwoDigits(out, min)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = marshalTwoDigits(out, sec)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, offset := t.Zone()
|
||||
|
||||
switch {
|
||||
case offset/60 == 0:
|
||||
err = out.WriteByte('Z')
|
||||
return
|
||||
case offset > 0:
|
||||
err = out.WriteByte('+')
|
||||
case offset < 0:
|
||||
err = out.WriteByte('-')
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
offsetMinutes := offset / 60
|
||||
if offsetMinutes < 0 {
|
||||
offsetMinutes = -offsetMinutes
|
||||
}
|
||||
|
||||
err = marshalTwoDigits(out, offsetMinutes/60)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = marshalTwoDigits(out, offsetMinutes%60)
|
||||
return
|
||||
}
|
||||
|
||||
func stripTagAndLength(in []byte) []byte {
|
||||
_, offset, err := parseTagAndLength(in, 0)
|
||||
if err != nil {
|
||||
return in
|
||||
}
|
||||
return in[offset:]
|
||||
}
|
||||
|
||||
func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) {
|
||||
switch value.Type() {
|
||||
case flagType:
|
||||
return nil
|
||||
case timeType:
|
||||
t := value.Interface().(time.Time)
|
||||
if params.timeType == TagGeneralizedTime || outsideUTCRange(t) {
|
||||
return marshalGeneralizedTime(out, t)
|
||||
} else {
|
||||
return marshalUTCTime(out, t)
|
||||
}
|
||||
case bitStringType:
|
||||
return marshalBitString(out, value.Interface().(BitString))
|
||||
case objectIdentifierType:
|
||||
return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier))
|
||||
case bigIntType:
|
||||
return marshalBigInt(out, value.Interface().(*big.Int))
|
||||
}
|
||||
|
||||
switch v := value; v.Kind() {
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return out.WriteByte(255)
|
||||
} else {
|
||||
return out.WriteByte(0)
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return marshalInt64(out, v.Int())
|
||||
case reflect.Struct:
|
||||
t := v.Type()
|
||||
|
||||
startingField := 0
|
||||
|
||||
// If the first element of the structure is a non-empty
|
||||
// RawContents, then we don't bother serializing the rest.
|
||||
if t.NumField() > 0 && t.Field(0).Type == rawContentsType {
|
||||
s := v.Field(0)
|
||||
if s.Len() > 0 {
|
||||
bytes := make([]byte, s.Len())
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
bytes[i] = uint8(s.Index(i).Uint())
|
||||
}
|
||||
/* The RawContents will contain the tag and
|
||||
* length fields but we'll also be writing
|
||||
* those ourselves, so we strip them out of
|
||||
* bytes */
|
||||
_, err = out.Write(stripTagAndLength(bytes))
|
||||
return
|
||||
} else {
|
||||
startingField = 1
|
||||
}
|
||||
}
|
||||
|
||||
for i := startingField; i < t.NumField(); i++ {
|
||||
var pre *forkableWriter
|
||||
pre, out = out.fork()
|
||||
err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1")))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
case reflect.Slice:
|
||||
sliceType := v.Type()
|
||||
if sliceType.Elem().Kind() == reflect.Uint8 {
|
||||
bytes := make([]byte, v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
bytes[i] = uint8(v.Index(i).Uint())
|
||||
}
|
||||
_, err = out.Write(bytes)
|
||||
return
|
||||
}
|
||||
|
||||
// jtasn1 Pass on the tags to the members but need to unset explicit switch and implicit value
|
||||
//var fp fieldParameters
|
||||
params.explicit = false
|
||||
params.tag = nil
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
var pre *forkableWriter
|
||||
pre, out = out.fork()
|
||||
err = marshalField(pre, v.Index(i), params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
case reflect.String:
|
||||
switch params.stringType {
|
||||
case TagIA5String:
|
||||
return marshalIA5String(out, v.String())
|
||||
case TagPrintableString:
|
||||
return marshalPrintableString(out, v.String())
|
||||
default:
|
||||
return marshalUTF8String(out, v.String())
|
||||
}
|
||||
}
|
||||
|
||||
return StructuralError{"unknown Go type"}
|
||||
}
|
||||
|
||||
func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err error) {
|
||||
if !v.IsValid() {
|
||||
return fmt.Errorf("asn1: cannot marshal nil value")
|
||||
}
|
||||
// If the field is an interface{} then recurse into it.
|
||||
if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 {
|
||||
return marshalField(out, v.Elem(), params)
|
||||
}
|
||||
|
||||
if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
if params.optional && params.defaultValue != nil && canHaveDefaultValue(v.Kind()) {
|
||||
defaultValue := reflect.New(v.Type()).Elem()
|
||||
defaultValue.SetInt(*params.defaultValue)
|
||||
|
||||
if reflect.DeepEqual(v.Interface(), defaultValue.Interface()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If no default value is given then the zero value for the type is
|
||||
// assumed to be the default value. This isn't obviously the correct
|
||||
// behaviour, but it's what Go has traditionally done.
|
||||
if params.optional && params.defaultValue == nil {
|
||||
if reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if v.Type() == rawValueType {
|
||||
rv := v.Interface().(RawValue)
|
||||
if len(rv.FullBytes) != 0 {
|
||||
_, err = out.Write(rv.FullBytes)
|
||||
} else {
|
||||
err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = out.Write(rv.Bytes)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
tag, isCompound, ok := getUniversalType(v.Type())
|
||||
if !ok {
|
||||
err = StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())}
|
||||
return
|
||||
}
|
||||
class := ClassUniversal
|
||||
|
||||
if params.timeType != 0 && tag != TagUTCTime {
|
||||
return StructuralError{"explicit time type given to non-time member"}
|
||||
}
|
||||
|
||||
// jtasn1 updated to allow slices of strings
|
||||
if params.stringType != 0 && !(tag == TagPrintableString || (v.Kind() == reflect.Slice && tag == 16 && v.Type().Elem().Kind() == reflect.String)) {
|
||||
return StructuralError{"explicit string type given to non-string member"}
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case TagPrintableString:
|
||||
if params.stringType == 0 {
|
||||
// This is a string without an explicit string type. We'll use
|
||||
// a PrintableString if the character set in the string is
|
||||
// sufficiently limited, otherwise we'll use a UTF8String.
|
||||
for _, r := range v.String() {
|
||||
if r >= utf8.RuneSelf || !isPrintable(byte(r)) {
|
||||
if !utf8.ValidString(v.String()) {
|
||||
return errors.New("asn1: string not valid UTF-8")
|
||||
}
|
||||
tag = TagUTF8String
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tag = params.stringType
|
||||
}
|
||||
case TagUTCTime:
|
||||
if params.timeType == TagGeneralizedTime || outsideUTCRange(v.Interface().(time.Time)) {
|
||||
tag = TagGeneralizedTime
|
||||
}
|
||||
}
|
||||
|
||||
if params.set {
|
||||
if tag != TagSequence {
|
||||
return StructuralError{"non sequence tagged as set"}
|
||||
}
|
||||
tag = TagSet
|
||||
}
|
||||
|
||||
tags, body := out.fork()
|
||||
|
||||
err = marshalBody(body, v, params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
bodyLen := body.Len()
|
||||
|
||||
var explicitTag *forkableWriter
|
||||
if params.explicit {
|
||||
explicitTag, tags = tags.fork()
|
||||
}
|
||||
|
||||
if !params.explicit && params.tag != nil {
|
||||
// implicit tag.
|
||||
tag = *params.tag
|
||||
class = ClassContextSpecific
|
||||
}
|
||||
|
||||
err = marshalTagAndLength(tags, tagAndLength{class, tag, bodyLen, isCompound})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if params.explicit {
|
||||
err = marshalTagAndLength(explicitTag, tagAndLength{
|
||||
class: ClassContextSpecific,
|
||||
tag: *params.tag,
|
||||
length: bodyLen + tags.Len(),
|
||||
isCompound: true,
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Marshal returns the ASN.1 encoding of val.
|
||||
//
|
||||
// In addition to the struct tags recognised by Unmarshal, the following can be
|
||||
// used:
|
||||
//
|
||||
// ia5: causes strings to be marshaled as ASN.1, IA5 strings
|
||||
// omitempty: causes empty slices to be skipped
|
||||
// printable: causes strings to be marshaled as ASN.1, PrintableString strings.
|
||||
// utf8: causes strings to be marshaled as ASN.1, UTF8 strings
|
||||
func Marshal(val interface{}) ([]byte, error) {
|
||||
var out bytes.Buffer
|
||||
v := reflect.ValueOf(val)
|
||||
f := newForkableWriter()
|
||||
err := marshalField(f, v, fieldParameters{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = f.writeTo(&out)
|
||||
return out.Bytes(), err
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
|
||||
2898 / PKCS #5 v2.0.
|
||||
|
||||
A key derivation function is useful when encrypting data based on a password
|
||||
or any other not-fully-random data. It uses a pseudorandom function to derive
|
||||
a secure encryption key based on the password.
|
||||
|
||||
While v2.0 of the standard defines only one pseudorandom function to use,
|
||||
HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
|
||||
Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
|
||||
choose, you can pass the `New` functions from the different SHA packages to
|
||||
pbkdf2.Key.
|
||||
*/
|
||||
package pbkdf2
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Key derives a key from the password, salt and iteration count, returning a
|
||||
// []byte of length keylen that can be used as cryptographic key. The key is
|
||||
// derived based on the method described as PBKDF2 with the HMAC variant using
|
||||
// the supplied hash function.
|
||||
//
|
||||
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
|
||||
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
|
||||
// doing:
|
||||
//
|
||||
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
|
||||
//
|
||||
// Remember to get a good random salt. At least 8 bytes is recommended by the
|
||||
// RFC.
|
||||
//
|
||||
// Using a higher iteration count will increase the cost of an exhaustive
|
||||
// search but will also make derivation proportionally slower.
|
||||
func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
|
||||
return Key64(password, salt, int64(iter), int64(keyLen), h)
|
||||
}
|
||||
|
||||
// Key64 derives a key from the password, salt and iteration count, returning a
|
||||
// []byte of length keylen that can be used as cryptographic key. Key64 uses
|
||||
// int64 for the iteration count and key length to allow larger values.
|
||||
// The key is derived based on the method described as PBKDF2 with the HMAC
|
||||
// variant using the supplied hash function.
|
||||
//
|
||||
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
|
||||
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
|
||||
// doing:
|
||||
//
|
||||
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
|
||||
//
|
||||
// Remember to get a good random salt. At least 8 bytes is recommended by the
|
||||
// RFC.
|
||||
//
|
||||
// Using a higher iteration count will increase the cost of an exhaustive
|
||||
// search but will also make derivation proportionally slower.
|
||||
func Key64(password, salt []byte, iter, keyLen int64, h func() hash.Hash) []byte {
|
||||
prf := hmac.New(h, password)
|
||||
hashLen := int64(prf.Size())
|
||||
numBlocks := (keyLen + hashLen - 1) / hashLen
|
||||
|
||||
var buf [4]byte
|
||||
dk := make([]byte, 0, numBlocks*hashLen)
|
||||
U := make([]byte, hashLen)
|
||||
for block := int64(1); block <= numBlocks; block++ {
|
||||
// N.B.: || means concatenation, ^ means XOR
|
||||
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
|
||||
// U_1 = PRF(password, salt || uint(i))
|
||||
prf.Reset()
|
||||
prf.Write(salt)
|
||||
buf[0] = byte(block >> 24)
|
||||
buf[1] = byte(block >> 16)
|
||||
buf[2] = byte(block >> 8)
|
||||
buf[3] = byte(block)
|
||||
prf.Write(buf[:4])
|
||||
dk = prf.Sum(dk)
|
||||
T := dk[int64(len(dk))-hashLen:]
|
||||
copy(U, T)
|
||||
|
||||
// U_n = PRF(password, U_(n-1))
|
||||
for n := int64(2); n <= iter; n++ {
|
||||
prf.Reset()
|
||||
prf.Write(U)
|
||||
U = U[:0]
|
||||
U = prf.Sum(U)
|
||||
for x := range U {
|
||||
T[x] ^= U[x]
|
||||
}
|
||||
}
|
||||
}
|
||||
return dk[:keyLen]
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
// Copyright 2020 yiya1989. All rights reserved.
|
||||
// Apache License Version 2.0
|
||||
package krb5forssh
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/jcmturner/gokrb5.v7/client"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/config"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/crypto"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/gssapi"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/chksumtype"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/flags"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/keytab"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/messages"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/spnego"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/types"
|
||||
)
|
||||
|
||||
type Krb5ClientState int
|
||||
|
||||
const (
|
||||
ContextFlagREADY = 128
|
||||
/* initiator states */
|
||||
InitiatorStart Krb5ClientState = iota
|
||||
InitiatorRestart
|
||||
InitiatorWaitForMutal
|
||||
InitiatorReady
|
||||
)
|
||||
|
||||
func NewKrb5InitiatorClient(krb5Conf, user, realm string, keytabConf []byte) (kcl Krb5InitiatorClient, err error) {
|
||||
c, _ := config.NewConfigFromString(krb5Conf)
|
||||
|
||||
// Set to lookup KDCs in DNS
|
||||
c.LibDefaults.DNSLookupKDC = true
|
||||
|
||||
// Blank out the KDCs to ensure they are not being used
|
||||
c.Realms = []config.Realm{}
|
||||
|
||||
// Init keytab from conf
|
||||
kt := &keytab.Keytab{}
|
||||
if err = kt.Unmarshal([]byte(keytabConf)); err != nil {
|
||||
return kcl, fmt.Errorf("unmarshal keytabConf failed: %w", err)
|
||||
}
|
||||
|
||||
// Init krb5 client and login
|
||||
cl := client.NewClientWithKeytab(user, realm, kt, c)
|
||||
|
||||
err = cl.Login()
|
||||
if err != nil {
|
||||
fmt.Printf("error on logging in using DNS lookup of KDCs: %v\n", err)
|
||||
return
|
||||
}
|
||||
err = cl.AffirmLogin()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
return Krb5InitiatorClient{
|
||||
client: cl,
|
||||
state: InitiatorStart,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Krb5InitiatorClient struct {
|
||||
state Krb5ClientState
|
||||
client *client.Client
|
||||
subkey types.EncryptionKey
|
||||
}
|
||||
|
||||
// Create new authenticator checksum for kerberos MechToken
|
||||
func (k *Krb5InitiatorClient) newAuthenticatorChksum(flags []int) []byte {
|
||||
a := make([]byte, 24)
|
||||
binary.LittleEndian.PutUint32(a[:4], 16)
|
||||
for _, i := range flags {
|
||||
if i == gssapi.ContextFlagDeleg {
|
||||
x := make([]byte, 28-len(a))
|
||||
a = append(a, x...)
|
||||
}
|
||||
f := binary.LittleEndian.Uint32(a[20:24])
|
||||
f |= uint32(i)
|
||||
binary.LittleEndian.PutUint32(a[20:24], f)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (k *Krb5InitiatorClient) InitSecContext(target string, token []byte, isGSSDelegCreds bool) ([]byte, bool, error) {
|
||||
GSSAPIFlags := []int{
|
||||
ContextFlagREADY,
|
||||
gssapi.ContextFlagInteg,
|
||||
gssapi.ContextFlagMutual,
|
||||
}
|
||||
if isGSSDelegCreds {
|
||||
GSSAPIFlags = append(GSSAPIFlags, gssapi.ContextFlagDeleg)
|
||||
}
|
||||
APOptions := []int{flags.APOptionMutualRequired}
|
||||
|
||||
switch k.state {
|
||||
case InitiatorStart, InitiatorRestart:
|
||||
newTarget := strings.ReplaceAll(target, "@", "/")
|
||||
|
||||
tkt, sessionKey, err := k.client.GetServiceTicket(newTarget)
|
||||
if err != nil {
|
||||
return []byte{}, false, err
|
||||
}
|
||||
|
||||
krb5Token, err := spnego.NewKRB5TokenAPREQ(k.client, tkt, sessionKey, GSSAPIFlags, APOptions)
|
||||
|
||||
creds := k.client.Credentials
|
||||
auth, err := types.NewAuthenticator(creds.Domain(), creds.CName())
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("error generating new authenticator: %w", err)
|
||||
}
|
||||
auth.Cksum = types.Checksum{
|
||||
CksumType: chksumtype.GSSAPI,
|
||||
Checksum: k.newAuthenticatorChksum(GSSAPIFlags),
|
||||
}
|
||||
etype, _ := crypto.GetEtype(sessionKey.KeyType)
|
||||
if err := auth.GenerateSeqNumberAndSubKey(sessionKey.KeyType, etype.GetKeyByteSize()); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
k.subkey = auth.SubKey
|
||||
|
||||
APReq, err := messages.NewAPReq(
|
||||
tkt,
|
||||
sessionKey,
|
||||
auth,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("error generating NewAPReq: %w", err)
|
||||
}
|
||||
for _, o := range APOptions {
|
||||
types.SetFlag(&APReq.APOptions, o)
|
||||
}
|
||||
krb5Token.APReq = APReq
|
||||
|
||||
outToken, err := krb5Token.Marshal()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return []byte{}, false, err
|
||||
}
|
||||
k.state = InitiatorWaitForMutal
|
||||
return outToken, true, nil
|
||||
case InitiatorWaitForMutal:
|
||||
var krb5Token spnego.KRB5Token
|
||||
if err := krb5Token.Unmarshal(token); err != nil {
|
||||
err := fmt.Errorf("unmarshal APRep token failed: %w", err)
|
||||
return []byte{}, false, err
|
||||
}
|
||||
//var enc messages.EncAPRepPart
|
||||
//err2 := enc.Unmarshal(krb5Token.APRep.EncPart.Cipher)
|
||||
//fmt.Printf("err2: %#v, enc: %#v\n", err2, enc)
|
||||
|
||||
k.state = InitiatorReady
|
||||
return []byte{}, false, nil
|
||||
case InitiatorReady:
|
||||
return nil, false, fmt.Errorf("called one time too many, client has already been %d", k.state)
|
||||
default:
|
||||
return nil, false, fmt.Errorf("invalid state %d", k.state)
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Krb5InitiatorClient) GetMIC(micFiled []byte) ([]byte, error) {
|
||||
micToken, err := gssapi.NewInitiatorMICToken(micFiled, k.subkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token, err := micToken.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (k *Krb5InitiatorClient) DeleteSecContext() error {
|
||||
k.client.Destroy()
|
||||
return nil
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package md4 implements the MD4 hash algorithm as defined in RFC 1320.
|
||||
//
|
||||
// Deprecated: MD4 is cryptographically broken and should should only be used
|
||||
// where compatibility with legacy systems, not security, is the goal. Instead,
|
||||
// use a secure hash like SHA-256 (from crypto/sha256).
|
||||
package md4 // import "golang.org/x/crypto/md4"
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"hash"
|
||||
)
|
||||
|
||||
func init() {
|
||||
crypto.RegisterHash(crypto.MD4, New)
|
||||
}
|
||||
|
||||
// The size of an MD4 checksum in bytes.
|
||||
const Size = 16
|
||||
|
||||
// The blocksize of MD4 in bytes.
|
||||
const BlockSize = 64
|
||||
|
||||
const (
|
||||
_Chunk = 64
|
||||
_Init0 = 0x67452301
|
||||
_Init1 = 0xEFCDAB89
|
||||
_Init2 = 0x98BADCFE
|
||||
_Init3 = 0x10325476
|
||||
)
|
||||
|
||||
// digest represents the partial evaluation of a checksum.
|
||||
type digest struct {
|
||||
s [4]uint32
|
||||
x [_Chunk]byte
|
||||
nx int
|
||||
len uint64
|
||||
}
|
||||
|
||||
func (d *digest) Reset() {
|
||||
d.s[0] = _Init0
|
||||
d.s[1] = _Init1
|
||||
d.s[2] = _Init2
|
||||
d.s[3] = _Init3
|
||||
d.nx = 0
|
||||
d.len = 0
|
||||
}
|
||||
|
||||
// New returns a new hash.Hash computing the MD4 checksum.
|
||||
func New() hash.Hash {
|
||||
d := new(digest)
|
||||
d.Reset()
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *digest) Size() int { return Size }
|
||||
|
||||
func (d *digest) BlockSize() int { return BlockSize }
|
||||
|
||||
func (d *digest) Write(p []byte) (nn int, err error) {
|
||||
nn = len(p)
|
||||
d.len += uint64(nn)
|
||||
if d.nx > 0 {
|
||||
n := len(p)
|
||||
if n > _Chunk-d.nx {
|
||||
n = _Chunk - d.nx
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
d.x[d.nx+i] = p[i]
|
||||
}
|
||||
d.nx += n
|
||||
if d.nx == _Chunk {
|
||||
_Block(d, d.x[0:])
|
||||
d.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
n := _Block(d, p)
|
||||
p = p[n:]
|
||||
if len(p) > 0 {
|
||||
d.nx = copy(d.x[:], p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d0 *digest) Sum(in []byte) []byte {
|
||||
// Make a copy of d0, so that caller can keep writing and summing.
|
||||
d := new(digest)
|
||||
*d = *d0
|
||||
|
||||
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
|
||||
len := d.len
|
||||
var tmp [64]byte
|
||||
tmp[0] = 0x80
|
||||
if len%64 < 56 {
|
||||
d.Write(tmp[0 : 56-len%64])
|
||||
} else {
|
||||
d.Write(tmp[0 : 64+56-len%64])
|
||||
}
|
||||
|
||||
// Length in bits.
|
||||
len <<= 3
|
||||
for i := uint(0); i < 8; i++ {
|
||||
tmp[i] = byte(len >> (8 * i))
|
||||
}
|
||||
d.Write(tmp[0:8])
|
||||
|
||||
if d.nx != 0 {
|
||||
panic("d.nx != 0")
|
||||
}
|
||||
|
||||
for _, s := range d.s {
|
||||
in = append(in, byte(s>>0))
|
||||
in = append(in, byte(s>>8))
|
||||
in = append(in, byte(s>>16))
|
||||
in = append(in, byte(s>>24))
|
||||
}
|
||||
return in
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// MD4 block step.
|
||||
// In its own file so that a faster assembly or C version
|
||||
// can be substituted easily.
|
||||
|
||||
package md4
|
||||
|
||||
var shift1 = []uint{3, 7, 11, 19}
|
||||
var shift2 = []uint{3, 5, 9, 13}
|
||||
var shift3 = []uint{3, 9, 11, 15}
|
||||
|
||||
var xIndex2 = []uint{0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15}
|
||||
var xIndex3 = []uint{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}
|
||||
|
||||
func _Block(dig *digest, p []byte) int {
|
||||
a := dig.s[0]
|
||||
b := dig.s[1]
|
||||
c := dig.s[2]
|
||||
d := dig.s[3]
|
||||
n := 0
|
||||
var X [16]uint32
|
||||
for len(p) >= _Chunk {
|
||||
aa, bb, cc, dd := a, b, c, d
|
||||
|
||||
j := 0
|
||||
for i := 0; i < 16; i++ {
|
||||
X[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24
|
||||
j += 4
|
||||
}
|
||||
|
||||
// If this needs to be made faster in the future,
|
||||
// the usual trick is to unroll each of these
|
||||
// loops by a factor of 4; that lets you replace
|
||||
// the shift[] lookups with constants and,
|
||||
// with suitable variable renaming in each
|
||||
// unrolled body, delete the a, b, c, d = d, a, b, c
|
||||
// (or you can let the optimizer do the renaming).
|
||||
//
|
||||
// The index variables are uint so that % by a power
|
||||
// of two can be optimized easily by a compiler.
|
||||
|
||||
// Round 1.
|
||||
for i := uint(0); i < 16; i++ {
|
||||
x := i
|
||||
s := shift1[i%4]
|
||||
f := ((c ^ d) & b) ^ d
|
||||
a += f + X[x]
|
||||
a = a<<s | a>>(32-s)
|
||||
a, b, c, d = d, a, b, c
|
||||
}
|
||||
|
||||
// Round 2.
|
||||
for i := uint(0); i < 16; i++ {
|
||||
x := xIndex2[i]
|
||||
s := shift2[i%4]
|
||||
g := (b & c) | (b & d) | (c & d)
|
||||
a += g + X[x] + 0x5a827999
|
||||
a = a<<s | a>>(32-s)
|
||||
a, b, c, d = d, a, b, c
|
||||
}
|
||||
|
||||
// Round 3.
|
||||
for i := uint(0); i < 16; i++ {
|
||||
x := xIndex3[i]
|
||||
s := shift3[i%4]
|
||||
h := b ^ c ^ d
|
||||
a += h + X[x] + 0x6ed9eba1
|
||||
a = a<<s | a>>(32-s)
|
||||
a, b, c, d = d, a, b, c
|
||||
}
|
||||
|
||||
a += aa
|
||||
b += bb
|
||||
c += cc
|
||||
d += dd
|
||||
|
||||
p = p[_Chunk:]
|
||||
n += _Chunk
|
||||
}
|
||||
|
||||
dig.s[0] = a
|
||||
dig.s[1] = b
|
||||
dig.s[2] = c
|
||||
dig.s[3] = d
|
||||
return n
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
|
||||
2898 / PKCS #5 v2.0.
|
||||
|
||||
A key derivation function is useful when encrypting data based on a password
|
||||
or any other not-fully-random data. It uses a pseudorandom function to derive
|
||||
a secure encryption key based on the password.
|
||||
|
||||
While v2.0 of the standard defines only one pseudorandom function to use,
|
||||
HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
|
||||
Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
|
||||
choose, you can pass the `New` functions from the different SHA packages to
|
||||
pbkdf2.Key.
|
||||
*/
|
||||
package pbkdf2 // import "golang.org/x/crypto/pbkdf2"
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Key derives a key from the password, salt and iteration count, returning a
|
||||
// []byte of length keylen that can be used as cryptographic key. The key is
|
||||
// derived based on the method described as PBKDF2 with the HMAC variant using
|
||||
// the supplied hash function.
|
||||
//
|
||||
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
|
||||
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
|
||||
// doing:
|
||||
//
|
||||
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
|
||||
//
|
||||
// Remember to get a good random salt. At least 8 bytes is recommended by the
|
||||
// RFC.
|
||||
//
|
||||
// Using a higher iteration count will increase the cost of an exhaustive
|
||||
// search but will also make derivation proportionally slower.
|
||||
func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
|
||||
prf := hmac.New(h, password)
|
||||
hashLen := prf.Size()
|
||||
numBlocks := (keyLen + hashLen - 1) / hashLen
|
||||
|
||||
var buf [4]byte
|
||||
dk := make([]byte, 0, numBlocks*hashLen)
|
||||
U := make([]byte, hashLen)
|
||||
for block := 1; block <= numBlocks; block++ {
|
||||
// N.B.: || means concatenation, ^ means XOR
|
||||
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
|
||||
// U_1 = PRF(password, salt || uint(i))
|
||||
prf.Reset()
|
||||
prf.Write(salt)
|
||||
buf[0] = byte(block >> 24)
|
||||
buf[1] = byte(block >> 16)
|
||||
buf[2] = byte(block >> 8)
|
||||
buf[3] = byte(block)
|
||||
prf.Write(buf[:4])
|
||||
dk = prf.Sum(dk)
|
||||
T := dk[len(dk)-hashLen:]
|
||||
copy(U, T)
|
||||
|
||||
// U_n = PRF(password, U_(n-1))
|
||||
for n := 2; n <= iter; n++ {
|
||||
prf.Reset()
|
||||
prf.Write(U)
|
||||
U = U[:0]
|
||||
U = prf.Sum(U)
|
||||
for x := range U {
|
||||
T[x] ^= U[x]
|
||||
}
|
||||
}
|
||||
}
|
||||
return dk[:keyLen]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# AES CBC Ciphertext Stealing
|
||||
[](https://godoc.org/gopkg.in/jcmturner/aescts.v1) [](https://goreportcard.com/report/gopkg.in/jcmturner/aescts.v1)
|
||||
|
||||
Encrypt and decrypt data using AES CBC Ciphertext stealing mode.
|
||||
|
||||
Reference: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
|
||||
|
||||
To get the package, execute:
|
||||
```
|
||||
go get gopkg.in/jcmturner/aescts.v1
|
||||
```
|
||||
To import this package, add the following line to your code:
|
||||
```go
|
||||
import "gopkg.in/jcmturner/aescts.v1"
|
||||
|
||||
```
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
// Package aescts provides AES CBC CipherText Stealing encryption and decryption methods
|
||||
package aescts
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Encrypt the message with the key and the initial vector.
|
||||
// Returns: next iv, ciphertext bytes, error
|
||||
func Encrypt(key, iv, plaintext []byte) ([]byte, []byte, error) {
|
||||
l := len(plaintext)
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return []byte{}, []byte{}, fmt.Errorf("Error creating cipher: %v", err)
|
||||
}
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
|
||||
m := make([]byte, len(plaintext))
|
||||
copy(m, plaintext)
|
||||
|
||||
/*For consistency, ciphertext stealing is always used for the last two
|
||||
blocks of the data to be encrypted, as in [RC5]. If the data length
|
||||
is a multiple of the block size, this is equivalent to plain CBC mode
|
||||
with the last two ciphertext blocks swapped.*/
|
||||
/*The initial vector carried out from one encryption for use in a
|
||||
subsequent encryption is the next-to-last block of the encryption
|
||||
output; this is the encrypted form of the last plaintext block.*/
|
||||
if l <= aes.BlockSize {
|
||||
m, _ = zeroPad(m, aes.BlockSize)
|
||||
mode.CryptBlocks(m, m)
|
||||
return m, m, nil
|
||||
}
|
||||
if l%aes.BlockSize == 0 {
|
||||
mode.CryptBlocks(m, m)
|
||||
iv = m[len(m)-aes.BlockSize:]
|
||||
rb, _ := swapLastTwoBlocks(m, aes.BlockSize)
|
||||
return iv, rb, nil
|
||||
}
|
||||
m, _ = zeroPad(m, aes.BlockSize)
|
||||
rb, pb, lb, err := tailBlocks(m, aes.BlockSize)
|
||||
if err != nil {
|
||||
return []byte{}, []byte{}, fmt.Errorf("Error tailing blocks: %v", err)
|
||||
}
|
||||
var ct []byte
|
||||
if rb != nil {
|
||||
// Encrpt all but the lats 2 blocks and update the rolling iv
|
||||
mode.CryptBlocks(rb, rb)
|
||||
iv = rb[len(rb)-aes.BlockSize:]
|
||||
mode = cipher.NewCBCEncrypter(block, iv)
|
||||
ct = append(ct, rb...)
|
||||
}
|
||||
mode.CryptBlocks(pb, pb)
|
||||
mode = cipher.NewCBCEncrypter(block, pb)
|
||||
mode.CryptBlocks(lb, lb)
|
||||
// Cipher Text Stealing (CTS) - Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
|
||||
// Swap the last two cipher blocks
|
||||
// Truncate the ciphertext to the length of the original plaintext
|
||||
ct = append(ct, lb...)
|
||||
ct = append(ct, pb...)
|
||||
return lb, ct[:l], nil
|
||||
}
|
||||
|
||||
// Decrypt the ciphertext with the key and the initial vector.
|
||||
func Decrypt(key, iv, ciphertext []byte) ([]byte, error) {
|
||||
// Copy the cipher text as golang slices even when passed by value to this method can result in the backing arrays of the calling code value being updated.
|
||||
ct := make([]byte, len(ciphertext))
|
||||
copy(ct, ciphertext)
|
||||
if len(ct) < aes.BlockSize {
|
||||
return []byte{}, fmt.Errorf("Ciphertext is not large enough. It is less that one block size. Blocksize:%v; Ciphertext:%v", aes.BlockSize, len(ct))
|
||||
}
|
||||
// Configure the CBC
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating cipher: %v", err)
|
||||
}
|
||||
var mode cipher.BlockMode
|
||||
|
||||
//If ciphertext is multiple of blocksize we just need to swap back the last two blocks and then do CBC
|
||||
//If the ciphertext is just one block we can't swap so we just decrypt
|
||||
if len(ct)%aes.BlockSize == 0 {
|
||||
if len(ct) > aes.BlockSize {
|
||||
ct, _ = swapLastTwoBlocks(ct, aes.BlockSize)
|
||||
}
|
||||
mode = cipher.NewCBCDecrypter(block, iv)
|
||||
message := make([]byte, len(ct))
|
||||
mode.CryptBlocks(message, ct)
|
||||
return message[:len(ct)], nil
|
||||
}
|
||||
|
||||
// Cipher Text Stealing (CTS) using CBC interface. Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
|
||||
// Get ciphertext of the 2nd to last (penultimate) block (cpb), the last block (clb) and the rest (crb)
|
||||
crb, cpb, clb, _ := tailBlocks(ct, aes.BlockSize)
|
||||
v := make([]byte, len(iv), len(iv))
|
||||
copy(v, iv)
|
||||
var message []byte
|
||||
if crb != nil {
|
||||
//If there is more than just the last and the penultimate block we decrypt it and the last bloc of this becomes the iv for later
|
||||
rb := make([]byte, len(crb))
|
||||
mode = cipher.NewCBCDecrypter(block, v)
|
||||
v = crb[len(crb)-aes.BlockSize:]
|
||||
mode.CryptBlocks(rb, crb)
|
||||
message = append(message, rb...)
|
||||
}
|
||||
|
||||
// We need to modify the cipher text
|
||||
// Decryt the 2nd to last (penultimate) block with a the original iv
|
||||
pb := make([]byte, aes.BlockSize)
|
||||
mode = cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(pb, cpb)
|
||||
// number of byte needed to pad
|
||||
npb := aes.BlockSize - len(ct)%aes.BlockSize
|
||||
//pad last block using the number of bytes needed from the tail of the plaintext 2nd to last (penultimate) block
|
||||
clb = append(clb, pb[len(pb)-npb:]...)
|
||||
|
||||
// Now decrypt the last block in the penultimate position (iv will be from the crb, if the is no crb it's zeros)
|
||||
// iv for the penultimate block decrypted in the last position becomes the modified last block
|
||||
lb := make([]byte, aes.BlockSize)
|
||||
mode = cipher.NewCBCDecrypter(block, v)
|
||||
v = clb
|
||||
mode.CryptBlocks(lb, clb)
|
||||
message = append(message, lb...)
|
||||
|
||||
// Now decrypt the penultimate block in the last position (iv will be from the modified last block)
|
||||
mode = cipher.NewCBCDecrypter(block, v)
|
||||
mode.CryptBlocks(cpb, cpb)
|
||||
message = append(message, cpb...)
|
||||
|
||||
// Truncate to the size of the original cipher text
|
||||
return message[:len(ct)], nil
|
||||
}
|
||||
|
||||
func tailBlocks(b []byte, c int) ([]byte, []byte, []byte, error) {
|
||||
if len(b) <= c {
|
||||
return []byte{}, []byte{}, []byte{}, errors.New("bytes slice is not larger than one block so cannot tail")
|
||||
}
|
||||
// Get size of last block
|
||||
var lbs int
|
||||
if l := len(b) % aes.BlockSize; l == 0 {
|
||||
lbs = aes.BlockSize
|
||||
} else {
|
||||
lbs = l
|
||||
}
|
||||
// Get last block
|
||||
lb := b[len(b)-lbs:]
|
||||
// Get 2nd to last (penultimate) block
|
||||
pb := b[len(b)-lbs-c : len(b)-lbs]
|
||||
if len(b) > 2*c {
|
||||
rb := b[:len(b)-lbs-c]
|
||||
return rb, pb, lb, nil
|
||||
}
|
||||
return nil, pb, lb, nil
|
||||
}
|
||||
|
||||
func swapLastTwoBlocks(b []byte, c int) ([]byte, error) {
|
||||
rb, pb, lb, err := tailBlocks(b, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []byte
|
||||
if rb != nil {
|
||||
out = append(out, rb...)
|
||||
}
|
||||
out = append(out, lb...)
|
||||
out = append(out, pb...)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// zeroPad pads bytes with zeros to nearest multiple of message size m.
|
||||
func zeroPad(b []byte, m int) ([]byte, error) {
|
||||
if m <= 0 {
|
||||
return nil, errors.New("Invalid message block size when padding")
|
||||
}
|
||||
if b == nil || len(b) == 0 {
|
||||
return nil, errors.New("Data not valid to pad: Zero size")
|
||||
}
|
||||
if l := len(b) % m; l != 0 {
|
||||
n := m - l
|
||||
z := make([]byte, n)
|
||||
b = append(b, z...)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- master
|
||||
|
||||
gobuild_args: -tags=integration -race
|
||||
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
before_install:
|
||||
- docker pull jcmturner/gokrb5:dns
|
||||
- docker run -d -h kdc.test.gokrb5 -v /etc/localtime:/etc/localtime:ro -e "TEST_KDC_ADDR=127.0.0.1" -p 53:53 -p 53:53/udp --name dns jcmturner/gokrb5:dns
|
||||
|
||||
before_script:
|
||||
- sudo sed -i 's/nameserver .*/nameserver 127.0.0.1/g' /etc/resolv.conf
|
||||
|
||||
env:
|
||||
- DNSUTILS_OVERRIDE_NS="127.0.0.1:53"
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package dnsutils
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// OrderedSRV returns a count of the results and a map keyed on the order they should be used.
|
||||
// This based on the records' priority and randomised selection based on their relative weighting.
|
||||
// The function's inputs are the same as those for net.LookupSRV
|
||||
// To use in the correct order:
|
||||
//
|
||||
// count, orderedSRV, err := OrderedSRV(service, proto, name)
|
||||
// i := 1
|
||||
// for i <= count {
|
||||
// srv := orderedSRV[i]
|
||||
// // Do something such as dial this SRV. If fails move on the the next or break if it succeeds.
|
||||
// i += 1
|
||||
// }
|
||||
func OrderedSRV(service, proto, name string) (int, map[int]*net.SRV, error) {
|
||||
_, addrs, err := net.LookupSRV(service, proto, name)
|
||||
if err != nil {
|
||||
return 0, make(map[int]*net.SRV), err
|
||||
}
|
||||
index, osrv := orderSRV(addrs)
|
||||
return index, osrv, nil
|
||||
}
|
||||
|
||||
func orderSRV(addrs []*net.SRV) (int, map[int]*net.SRV) {
|
||||
// Initialise the ordered map
|
||||
var o int
|
||||
osrv := make(map[int]*net.SRV)
|
||||
|
||||
prioMap := make(map[int][]*net.SRV, 0)
|
||||
for _, srv := range addrs {
|
||||
prioMap[int(srv.Priority)] = append(prioMap[int(srv.Priority)], srv)
|
||||
}
|
||||
|
||||
priorities := make([]int, 0)
|
||||
for p := range prioMap {
|
||||
priorities = append(priorities, p)
|
||||
}
|
||||
|
||||
var count int
|
||||
sort.Ints(priorities)
|
||||
for _, p := range priorities {
|
||||
tos := weightedOrder(prioMap[p])
|
||||
for i, s := range tos {
|
||||
count += 1
|
||||
osrv[o+i] = s
|
||||
}
|
||||
o += len(tos)
|
||||
}
|
||||
return count, osrv
|
||||
}
|
||||
|
||||
func weightedOrder(srvs []*net.SRV) map[int]*net.SRV {
|
||||
// Get the total weight
|
||||
var tw int
|
||||
for _, s := range srvs {
|
||||
tw += int(s.Weight)
|
||||
}
|
||||
|
||||
// Initialise the ordered map
|
||||
o := 1
|
||||
osrv := make(map[int]*net.SRV)
|
||||
|
||||
// Whilst there are still entries to be ordered
|
||||
l := len(srvs)
|
||||
for l > 0 {
|
||||
i := rand.Intn(l)
|
||||
s := srvs[i]
|
||||
var rw int
|
||||
if tw > 0 {
|
||||
// Greater the weight the more likely this will be zero or less
|
||||
rw = rand.Intn(tw) - int(s.Weight)
|
||||
}
|
||||
if rw <= 0 {
|
||||
// Put entry in position
|
||||
osrv[o] = s
|
||||
if len(srvs) > 1 {
|
||||
// Remove the entry from the source slice by swapping with the last entry and truncating
|
||||
srvs[len(srvs)-1], srvs[i] = srvs[i], srvs[len(srvs)-1]
|
||||
srvs = srvs[:len(srvs)-1]
|
||||
l = len(srvs)
|
||||
} else {
|
||||
l = 0
|
||||
}
|
||||
o += 1
|
||||
tw = tw - int(s.Weight)
|
||||
}
|
||||
}
|
||||
return osrv
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# goidentity
|
||||
|
||||
Standard interface to holding authenticated identities and their attributes.
|
||||
|
||||
To get the package, execute:
|
||||
```
|
||||
go get gopkg.in/jcmturner/goidentity.v3
|
||||
```
|
||||
To import this package, add the following line to your code:
|
||||
```go
|
||||
import "gopkg.in/jcmturner/goidentity.v3"
|
||||
|
||||
```
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package goidentity
|
||||
|
||||
type Authenticator interface {
|
||||
Authenticate() (Identity, bool, error)
|
||||
Mechanism() string // gives the name of the type of authentication mechanism
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package goidentity
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
CTXKey = "jcmturner/goidentity"
|
||||
)
|
||||
|
||||
type Identity interface {
|
||||
UserName() string
|
||||
SetUserName(s string)
|
||||
Domain() string
|
||||
SetDomain(s string)
|
||||
DisplayName() string
|
||||
SetDisplayName(s string)
|
||||
Human() bool
|
||||
SetHuman(b bool)
|
||||
AuthTime() time.Time
|
||||
SetAuthTime(t time.Time)
|
||||
AuthzAttributes() []string
|
||||
AddAuthzAttribute(a string)
|
||||
RemoveAuthzAttribute(a string)
|
||||
Authenticated() bool
|
||||
SetAuthenticated(b bool)
|
||||
Authorized(a string) bool
|
||||
SessionID() string
|
||||
Expired() bool
|
||||
Attributes() map[string]interface{}
|
||||
SetAttribute(k string, v interface{})
|
||||
SetAttributes(map[string]interface{})
|
||||
RemoveAttribute(k string)
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package goidentity
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
authenticated bool
|
||||
domain string
|
||||
userName string
|
||||
displayName string
|
||||
email string
|
||||
human bool
|
||||
groupMembership map[string]bool
|
||||
authTime time.Time
|
||||
sessionID string
|
||||
expiry time.Time
|
||||
attributes map[string]interface{}
|
||||
}
|
||||
|
||||
func NewUser(username string) User {
|
||||
uuid, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
uuid = "00unique-sess-ions-uuid-unavailable0"
|
||||
}
|
||||
return User{
|
||||
userName: username,
|
||||
groupMembership: make(map[string]bool),
|
||||
sessionID: uuid,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) UserName() string {
|
||||
return u.userName
|
||||
}
|
||||
|
||||
func (u *User) SetUserName(s string) {
|
||||
u.userName = s
|
||||
}
|
||||
|
||||
func (u *User) Domain() string {
|
||||
return u.domain
|
||||
}
|
||||
|
||||
func (u *User) SetDomain(s string) {
|
||||
u.domain = s
|
||||
}
|
||||
|
||||
func (u *User) DisplayName() string {
|
||||
if u.displayName == "" {
|
||||
return u.userName
|
||||
}
|
||||
return u.displayName
|
||||
}
|
||||
|
||||
func (u *User) SetDisplayName(s string) {
|
||||
u.displayName = s
|
||||
}
|
||||
|
||||
func (u *User) Human() bool {
|
||||
return u.human
|
||||
}
|
||||
|
||||
func (u *User) SetHuman(b bool) {
|
||||
u.human = b
|
||||
}
|
||||
|
||||
func (u *User) AuthTime() time.Time {
|
||||
return u.authTime
|
||||
}
|
||||
|
||||
func (u *User) SetAuthTime(t time.Time) {
|
||||
u.authTime = t
|
||||
}
|
||||
|
||||
func (u *User) AuthzAttributes() []string {
|
||||
s := make([]string, len(u.groupMembership))
|
||||
i := 0
|
||||
for a := range u.groupMembership {
|
||||
s[i] = a
|
||||
i++
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (u *User) Authenticated() bool {
|
||||
return u.authenticated
|
||||
}
|
||||
|
||||
func (u *User) SetAuthenticated(b bool) {
|
||||
u.authenticated = b
|
||||
}
|
||||
|
||||
func (u *User) AddAuthzAttribute(a string) {
|
||||
u.groupMembership[a] = true
|
||||
}
|
||||
|
||||
func (u *User) RemoveAuthzAttribute(a string) {
|
||||
if _, ok := u.groupMembership[a]; !ok {
|
||||
return
|
||||
}
|
||||
delete(u.groupMembership, a)
|
||||
}
|
||||
|
||||
func (u *User) EnableAuthzAttribute(a string) {
|
||||
if enabled, ok := u.groupMembership[a]; ok && !enabled {
|
||||
u.groupMembership[a] = true
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) DisableAuthzAttribute(a string) {
|
||||
if enabled, ok := u.groupMembership[a]; ok && enabled {
|
||||
u.groupMembership[a] = false
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) Authorized(a string) bool {
|
||||
if enabled, ok := u.groupMembership[a]; ok && enabled {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *User) SessionID() string {
|
||||
return u.sessionID
|
||||
}
|
||||
|
||||
func (u *User) SetExpiry(t time.Time) {
|
||||
u.expiry = t
|
||||
}
|
||||
|
||||
func (u *User) Expired() bool {
|
||||
if !u.expiry.IsZero() && time.Now().UTC().After(u.expiry) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *User) Attributes() map[string]interface{} {
|
||||
return u.attributes
|
||||
}
|
||||
|
||||
func (u *User) SetAttribute(k string, v interface{}) {
|
||||
u.attributes[k] = v
|
||||
}
|
||||
|
||||
func (u *User) SetAttributes(a map[string]interface{}) {
|
||||
u.attributes = a
|
||||
}
|
||||
|
||||
func (u *User) RemoveAttribute(k string) {
|
||||
delete(u.attributes, k)
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// Package asn1tools provides tools for managing ASN1 marshaled data.
|
||||
package asn1tools
|
||||
|
||||
import (
|
||||
"github.com/jcmturner/gofork/encoding/asn1"
|
||||
)
|
||||
|
||||
// MarshalLengthBytes returns the ASN1 encoded bytes for the length 'l'
|
||||
//
|
||||
// There are two forms: short (for lengths between 0 and 127), and long definite (for lengths between 0 and 2^1008 -1).
|
||||
//
|
||||
// Short form: One octet. Bit 8 has value "0" and bits 7-1 give the length.
|
||||
//
|
||||
// Long form: Two to 127 octets. Bit 8 of first octet has value "1" and bits 7-1 give the number of additional length octets. Second and following octets give the length, base 256, most significant digit first.
|
||||
func MarshalLengthBytes(l int) []byte {
|
||||
if l <= 127 {
|
||||
return []byte{byte(l)}
|
||||
}
|
||||
var b []byte
|
||||
p := 1
|
||||
for i := 1; i < 127; {
|
||||
b = append([]byte{byte((l % (p * 256)) / p)}, b...)
|
||||
p = p * 256
|
||||
l = l - l%p
|
||||
if l <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return append([]byte{byte(128 + len(b))}, b...)
|
||||
}
|
||||
|
||||
// GetLengthFromASN returns the length of a slice of ASN1 encoded bytes from the ASN1 length header it contains.
|
||||
func GetLengthFromASN(b []byte) int {
|
||||
if int(b[1]) <= 127 {
|
||||
return int(b[1])
|
||||
}
|
||||
// The bytes that indicate the length
|
||||
lb := b[2 : 2+int(b[1])-128]
|
||||
base := 1
|
||||
l := 0
|
||||
for i := len(lb) - 1; i >= 0; i-- {
|
||||
l += int(lb[i]) * base
|
||||
base = base * 256
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// GetNumberBytesInLengthHeader returns the number of bytes in the ASn1 header that indicate the length.
|
||||
func GetNumberBytesInLengthHeader(b []byte) int {
|
||||
if int(b[1]) <= 127 {
|
||||
return 1
|
||||
}
|
||||
// The bytes that indicate the length
|
||||
return 1 + int(b[1]) - 128
|
||||
}
|
||||
|
||||
// AddASNAppTag adds an ASN1 encoding application tag value to the raw bytes provided.
|
||||
func AddASNAppTag(b []byte, tag int) []byte {
|
||||
r := asn1.RawValue{
|
||||
Class: asn1.ClassApplication,
|
||||
IsCompound: true,
|
||||
Tag: tag,
|
||||
Bytes: b,
|
||||
}
|
||||
ab, _ := asn1.Marshal(r)
|
||||
return ab
|
||||
}
|
||||
|
||||
/*
|
||||
// The Marshal method of golang's asn1 package does not enable you to define wrapping the output in an application tag.
|
||||
// This method adds that wrapping tag.
|
||||
func AddASNAppTag(b []byte, tag int) []byte {
|
||||
// The ASN1 wrapping consists of 2 bytes:
|
||||
// 1st byte -> Identifier Octet - Application Tag
|
||||
// 2nd byte -> The length (this will be the size indicated in the input bytes + 2 for the additional bytes we add here.
|
||||
// Application Tag:
|
||||
//| Bit: | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
|
||||
//| Value: | 0 | 1 | 1 | From the RFC spec 4120 |
|
||||
//| Explanation | Defined by the ASN1 encoding rules for an application tag | A value of 1 indicates a constructed type | The ASN Application tag value |
|
||||
// Therefore the value of the byte is an integer = ( Application tag value + 96 )
|
||||
//b = append(MarshalLengthBytes(int(b[1])+2), b...)
|
||||
b = append(MarshalLengthBytes(len(b)), b...)
|
||||
b = append([]byte{byte(96 + tag)}, b...)
|
||||
return b
|
||||
}
|
||||
*/
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"gopkg.in/jcmturner/gokrb5.v7/crypto"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/crypto/etype"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/errorcode"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/keyusage"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/patype"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/krberror"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/messages"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/types"
|
||||
)
|
||||
|
||||
// ASExchange performs an AS exchange for the client to retrieve a TGT.
|
||||
func (cl *Client) ASExchange(realm string, ASReq messages.ASReq, referral int) (messages.ASRep, error) {
|
||||
if ok, err := cl.IsConfigured(); !ok {
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.ConfigError, "AS Exchange cannot be performed")
|
||||
}
|
||||
|
||||
// Set PAData if required
|
||||
err := setPAData(cl, nil, &ASReq)
|
||||
if err != nil {
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: issue with setting PAData on AS_REQ")
|
||||
}
|
||||
|
||||
b, err := ASReq.Marshal()
|
||||
if err != nil {
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ")
|
||||
}
|
||||
var ASRep messages.ASRep
|
||||
|
||||
rb, err := cl.sendToKDC(b, realm)
|
||||
if err != nil {
|
||||
if e, ok := err.(messages.KRBError); ok {
|
||||
switch e.ErrorCode {
|
||||
case errorcode.KDC_ERR_PREAUTH_REQUIRED, errorcode.KDC_ERR_PREAUTH_FAILED:
|
||||
// From now on assume this client will need to do this pre-auth and set the PAData
|
||||
cl.settings.assumePreAuthentication = true
|
||||
err = setPAData(cl, &e, &ASReq)
|
||||
if err != nil {
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: failed setting AS_REQ PAData for pre-authentication required")
|
||||
}
|
||||
b, err := ASReq.Marshal()
|
||||
if err != nil {
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ with PAData")
|
||||
}
|
||||
rb, err = cl.sendToKDC(b, realm)
|
||||
if err != nil {
|
||||
if _, ok := err.(messages.KRBError); ok {
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC")
|
||||
}
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC")
|
||||
}
|
||||
case errorcode.KDC_ERR_WRONG_REALM:
|
||||
// Client referral https://tools.ietf.org/html/rfc6806.html#section-7
|
||||
if referral > 5 {
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "maximum number of client referrals exceeded")
|
||||
}
|
||||
referral++
|
||||
return cl.ASExchange(e.CRealm, ASReq, referral)
|
||||
default:
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC")
|
||||
}
|
||||
} else {
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC")
|
||||
}
|
||||
}
|
||||
err = ASRep.Unmarshal(rb)
|
||||
if err != nil {
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed to process the AS_REP")
|
||||
}
|
||||
if ok, err := ASRep.Verify(cl.Config, cl.Credentials, ASReq); !ok {
|
||||
return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: AS_REP is not valid or client password/keytab incorrect")
|
||||
}
|
||||
return ASRep, nil
|
||||
}
|
||||
|
||||
// setPAData adds pre-authentication data to the AS_REQ.
|
||||
func setPAData(cl *Client, krberr *messages.KRBError, ASReq *messages.ASReq) error {
|
||||
if !cl.settings.DisablePAFXFAST() {
|
||||
pa := types.PAData{PADataType: patype.PA_REQ_ENC_PA_REP}
|
||||
ASReq.PAData = append(ASReq.PAData, pa)
|
||||
}
|
||||
if cl.settings.AssumePreAuthentication() {
|
||||
// Identify the etype to use to encrypt the PA Data
|
||||
var et etype.EType
|
||||
var err error
|
||||
var key types.EncryptionKey
|
||||
if krberr == nil {
|
||||
// This is not in response to an error from the KDC. It is preemptive or renewal
|
||||
// There is no KRB Error that tells us the etype to use
|
||||
etn := cl.settings.preAuthEType // Use the etype that may have previously been negotiated
|
||||
if etn == 0 {
|
||||
etn = int32(cl.Config.LibDefaults.PreferredPreauthTypes[0]) // Resort to config
|
||||
}
|
||||
et, err = crypto.GetEtype(etn)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
|
||||
}
|
||||
key, err = cl.Key(et, nil)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncryptingError, "error getting key from credentials")
|
||||
}
|
||||
} else {
|
||||
// Get the etype to use from the PA data in the KRBError e-data
|
||||
et, err = preAuthEType(krberr)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
|
||||
}
|
||||
cl.settings.preAuthEType = et.GetETypeID() // Set the etype that has been defined for potential future use
|
||||
key, err = cl.Key(et, krberr)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncryptingError, "error getting key from credentials")
|
||||
}
|
||||
}
|
||||
// Generate the PA data
|
||||
paTSb, err := types.GetPAEncTSEncAsnMarshalled()
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.KRBMsgError, "error creating PAEncTSEnc for Pre-Authentication")
|
||||
}
|
||||
//TODO (theme: KVNO from keytab) the kvno should not be hard coded to 1 as this hampers troubleshooting.
|
||||
paEncTS, err := crypto.GetEncryptedData(paTSb, key, keyusage.AS_REQ_PA_ENC_TIMESTAMP, 1)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncryptingError, "error encrypting pre-authentication timestamp")
|
||||
}
|
||||
pb, err := paEncTS.Marshal()
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.EncodingError, "error marshaling the PAEncTSEnc encrypted data")
|
||||
}
|
||||
pa := types.PAData{
|
||||
PADataType: patype.PA_ENC_TIMESTAMP,
|
||||
PADataValue: pb,
|
||||
}
|
||||
// Look for and delete any exiting patype.PA_ENC_TIMESTAMP
|
||||
for i, pa := range ASReq.PAData {
|
||||
if pa.PADataType == patype.PA_ENC_TIMESTAMP {
|
||||
ASReq.PAData[i] = ASReq.PAData[len(ASReq.PAData)-1]
|
||||
ASReq.PAData = ASReq.PAData[:len(ASReq.PAData)-1]
|
||||
}
|
||||
}
|
||||
ASReq.PAData = append(ASReq.PAData, pa)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// preAuthEType establishes what encryption type to use for pre-authentication from the KRBError returned from the KDC.
|
||||
func preAuthEType(krberr *messages.KRBError) (etype etype.EType, err error) {
|
||||
//The preferred ordering of the "hint" pre-authentication data that
|
||||
//affect client key selection is: ETYPE-INFO2, followed by ETYPE-INFO,
|
||||
//followed by PW-SALT.
|
||||
//A KDC SHOULD NOT send PA-PW-SALT when issuing a KRB-ERROR message
|
||||
//that requests additional pre-authentication. Implementation note:
|
||||
//Some KDC implementations issue an erroneous PA-PW-SALT when issuing a
|
||||
//KRB-ERROR message that requests additional pre-authentication.
|
||||
//Therefore, clients SHOULD ignore a PA-PW-SALT accompanying a
|
||||
//KRB-ERROR message that requests additional pre-authentication.
|
||||
var etypeID int32
|
||||
var pas types.PADataSequence
|
||||
e := pas.Unmarshal(krberr.EData)
|
||||
if e != nil {
|
||||
err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling KRBError data")
|
||||
return
|
||||
}
|
||||
for _, pa := range pas {
|
||||
switch pa.PADataType {
|
||||
case patype.PA_ETYPE_INFO2:
|
||||
info, e := pa.GetETypeInfo2()
|
||||
if e != nil {
|
||||
err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling ETYPE-INFO2 data")
|
||||
return
|
||||
}
|
||||
etypeID = info[0].EType
|
||||
break
|
||||
case patype.PA_ETYPE_INFO:
|
||||
info, e := pa.GetETypeInfo()
|
||||
if e != nil {
|
||||
err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling ETYPE-INFO data")
|
||||
return
|
||||
}
|
||||
etypeID = info[0].EType
|
||||
}
|
||||
}
|
||||
etype, e = crypto.GetEtype(etypeID)
|
||||
if e != nil {
|
||||
err = krberror.Errorf(e, krberror.EncryptingError, "error creating etype")
|
||||
return
|
||||
}
|
||||
return etype, nil
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/flags"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/nametype"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/krberror"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/messages"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/types"
|
||||
)
|
||||
|
||||
// TGSREQGenerateAndExchange generates the TGS_REQ and performs a TGS exchange to retrieve a ticket to the specified SPN.
|
||||
func (cl *Client) TGSREQGenerateAndExchange(spn types.PrincipalName, kdcRealm string, tgt messages.Ticket, sessionKey types.EncryptionKey, renewal bool) (tgsReq messages.TGSReq, tgsRep messages.TGSRep, err error) {
|
||||
tgsReq, err = messages.NewTGSReq(cl.Credentials.CName(), kdcRealm, cl.Config, tgt, sessionKey, spn, renewal)
|
||||
if err != nil {
|
||||
return tgsReq, tgsRep, krberror.Errorf(err, krberror.KRBMsgError, "TGS Exchange Error: failed to generate a new TGS_REQ")
|
||||
}
|
||||
return cl.TGSExchange(tgsReq, kdcRealm, tgsRep.Ticket, sessionKey, 0)
|
||||
}
|
||||
|
||||
// TGSExchange exchanges the provided TGS_REQ with the KDC to retrieve a TGS_REP.
|
||||
// Referrals are automatically handled.
|
||||
// The client's cache is updated with the ticket received.
|
||||
func (cl *Client) TGSExchange(tgsReq messages.TGSReq, kdcRealm string, tgt messages.Ticket, sessionKey types.EncryptionKey, referral int) (messages.TGSReq, messages.TGSRep, error) {
|
||||
var tgsRep messages.TGSRep
|
||||
b, err := tgsReq.Marshal()
|
||||
if err != nil {
|
||||
return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: failed to marshal TGS_REQ")
|
||||
}
|
||||
r, err := cl.sendToKDC(b, kdcRealm)
|
||||
if err != nil {
|
||||
if _, ok := err.(messages.KRBError); ok {
|
||||
return tgsReq, tgsRep, krberror.Errorf(err, krberror.KDCError, "TGS Exchange Error: kerberos error response from KDC when requesting for %s", tgsReq.ReqBody.SName.PrincipalNameString())
|
||||
}
|
||||
return tgsReq, tgsRep, krberror.Errorf(err, krberror.NetworkingError, "TGS Exchange Error: issue sending TGS_REQ to KDC")
|
||||
}
|
||||
err = tgsRep.Unmarshal(r)
|
||||
if err != nil {
|
||||
return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: failed to process the TGS_REP")
|
||||
}
|
||||
err = tgsRep.DecryptEncPart(sessionKey)
|
||||
if err != nil {
|
||||
return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: failed to process the TGS_REP")
|
||||
}
|
||||
if ok, err := tgsRep.Verify(cl.Config, tgsReq); !ok {
|
||||
return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: TGS_REP is not valid")
|
||||
}
|
||||
|
||||
if tgsRep.Ticket.SName.NameString[0] == "krbtgt" && !tgsRep.Ticket.SName.Equal(tgsReq.ReqBody.SName) {
|
||||
if referral > 5 {
|
||||
return tgsReq, tgsRep, krberror.Errorf(err, krberror.KRBMsgError, "TGS Exchange Error: maximum number of referrals exceeded")
|
||||
}
|
||||
// Server referral https://tools.ietf.org/html/rfc6806.html#section-8
|
||||
// The TGS Rep contains a TGT for another domain as the service resides in that domain.
|
||||
cl.addSession(tgsRep.Ticket, tgsRep.DecryptedEncPart)
|
||||
realm := tgsRep.Ticket.SName.NameString[len(tgsRep.Ticket.SName.NameString)-1]
|
||||
referral++
|
||||
if types.IsFlagSet(&tgsReq.ReqBody.KDCOptions, flags.EncTktInSkey) && len(tgsReq.ReqBody.AdditionalTickets) > 0 {
|
||||
tgsReq, err = messages.NewUser2UserTGSReq(cl.Credentials.CName(), kdcRealm, cl.Config, tgt, sessionKey, tgsReq.ReqBody.SName, tgsReq.Renewal, tgsReq.ReqBody.AdditionalTickets[0])
|
||||
if err != nil {
|
||||
return tgsReq, tgsRep, err
|
||||
}
|
||||
}
|
||||
tgsReq, err = messages.NewTGSReq(cl.Credentials.CName(), realm, cl.Config, tgsRep.Ticket, tgsRep.DecryptedEncPart.Key, tgsReq.ReqBody.SName, tgsReq.Renewal)
|
||||
if err != nil {
|
||||
return tgsReq, tgsRep, err
|
||||
}
|
||||
return cl.TGSExchange(tgsReq, realm, tgsRep.Ticket, tgsRep.DecryptedEncPart.Key, referral)
|
||||
}
|
||||
cl.cache.addEntry(
|
||||
tgsRep.Ticket,
|
||||
tgsRep.DecryptedEncPart.AuthTime,
|
||||
tgsRep.DecryptedEncPart.StartTime,
|
||||
tgsRep.DecryptedEncPart.EndTime,
|
||||
tgsRep.DecryptedEncPart.RenewTill,
|
||||
tgsRep.DecryptedEncPart.Key,
|
||||
)
|
||||
cl.Log("ticket added to cache for %s (EndTime: %v)", tgsRep.Ticket.SName.PrincipalNameString(), tgsRep.DecryptedEncPart.EndTime)
|
||||
return tgsReq, tgsRep, err
|
||||
}
|
||||
|
||||
// GetServiceTicket makes a request to get a service ticket for the SPN specified
|
||||
// SPN format: <SERVICE>/<FQDN> Eg. HTTP/www.example.com
|
||||
// The ticket will be added to the client's ticket cache
|
||||
func (cl *Client) GetServiceTicket(spn string) (messages.Ticket, types.EncryptionKey, error) {
|
||||
var tkt messages.Ticket
|
||||
var skey types.EncryptionKey
|
||||
if tkt, skey, ok := cl.GetCachedTicket(spn); ok {
|
||||
// Already a valid ticket in the cache
|
||||
return tkt, skey, nil
|
||||
}
|
||||
princ := types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, spn)
|
||||
realm := cl.Config.ResolveRealm(princ.NameString[len(princ.NameString)-1])
|
||||
|
||||
tgt, skey, err := cl.sessionTGT(realm)
|
||||
if err != nil {
|
||||
return tkt, skey, err
|
||||
}
|
||||
_, tgsRep, err := cl.TGSREQGenerateAndExchange(princ, realm, tgt, skey, false)
|
||||
if err != nil {
|
||||
return tkt, skey, err
|
||||
}
|
||||
return tgsRep.Ticket, tgsRep.DecryptedEncPart.Key, nil
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gopkg.in/jcmturner/gokrb5.v7/messages"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/types"
|
||||
)
|
||||
|
||||
// Cache for service tickets held by the client.
|
||||
type Cache struct {
|
||||
Entries map[string]CacheEntry
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
// CacheEntry holds details for a cache entry.
|
||||
type CacheEntry struct {
|
||||
Ticket messages.Ticket
|
||||
AuthTime time.Time
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
RenewTill time.Time
|
||||
SessionKey types.EncryptionKey
|
||||
}
|
||||
|
||||
// NewCache creates a new client ticket cache instance.
|
||||
func NewCache() *Cache {
|
||||
return &Cache{
|
||||
Entries: map[string]CacheEntry{},
|
||||
}
|
||||
}
|
||||
|
||||
// getEntry returns a cache entry that matches the SPN.
|
||||
func (c *Cache) getEntry(spn string) (CacheEntry, bool) {
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
e, ok := (*c).Entries[spn]
|
||||
return e, ok
|
||||
}
|
||||
|
||||
// addEntry adds a ticket to the cache.
|
||||
func (c *Cache) addEntry(tkt messages.Ticket, authTime, startTime, endTime, renewTill time.Time, sessionKey types.EncryptionKey) CacheEntry {
|
||||
spn := tkt.SName.PrincipalNameString()
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
(*c).Entries[spn] = CacheEntry{
|
||||
Ticket: tkt,
|
||||
AuthTime: authTime,
|
||||
StartTime: startTime,
|
||||
EndTime: endTime,
|
||||
RenewTill: renewTill,
|
||||
SessionKey: sessionKey,
|
||||
}
|
||||
return c.Entries[spn]
|
||||
}
|
||||
|
||||
// clear deletes all the cache entries
|
||||
func (c *Cache) clear() {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
for k := range c.Entries {
|
||||
delete(c.Entries, k)
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveEntry removes the cache entry for the defined SPN.
|
||||
func (c *Cache) RemoveEntry(spn string) {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
delete(c.Entries, spn)
|
||||
}
|
||||
|
||||
// GetCachedTicket returns a ticket from the cache for the SPN.
|
||||
// Only a ticket that is currently valid will be returned.
|
||||
func (cl *Client) GetCachedTicket(spn string) (messages.Ticket, types.EncryptionKey, bool) {
|
||||
if e, ok := cl.cache.getEntry(spn); ok {
|
||||
//If within time window of ticket return it
|
||||
if time.Now().UTC().After(e.StartTime) && time.Now().UTC().Before(e.EndTime) {
|
||||
cl.Log("ticket received from cache for %s", spn)
|
||||
return e.Ticket, e.SessionKey, true
|
||||
} else if time.Now().UTC().Before(e.RenewTill) {
|
||||
e, err := cl.renewTicket(e)
|
||||
if err != nil {
|
||||
return e.Ticket, e.SessionKey, false
|
||||
}
|
||||
return e.Ticket, e.SessionKey, true
|
||||
}
|
||||
}
|
||||
var tkt messages.Ticket
|
||||
var key types.EncryptionKey
|
||||
return tkt, key, false
|
||||
}
|
||||
|
||||
// renewTicket renews a cache entry ticket.
|
||||
// To renew from outside the client package use GetCachedTicket
|
||||
func (cl *Client) renewTicket(e CacheEntry) (CacheEntry, error) {
|
||||
spn := e.Ticket.SName
|
||||
_, _, err := cl.TGSREQGenerateAndExchange(spn, e.Ticket.Realm, e.Ticket, e.SessionKey, true)
|
||||
if err != nil {
|
||||
return e, err
|
||||
}
|
||||
e, ok := cl.cache.getEntry(e.Ticket.SName.PrincipalNameString())
|
||||
if !ok {
|
||||
return e, errors.New("ticket was not added to cache")
|
||||
}
|
||||
cl.Log("ticket renewed for %s (EndTime: %v)", spn.PrincipalNameString(), e.EndTime)
|
||||
return e, nil
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
// Package client provides a client library and methods for Kerberos 5 authentication.
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gopkg.in/jcmturner/gokrb5.v7/config"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/credentials"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/crypto"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/crypto/etype"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/errorcode"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/nametype"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/keytab"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/krberror"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/messages"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/types"
|
||||
)
|
||||
|
||||
// Client side configuration and state.
|
||||
type Client struct {
|
||||
Credentials *credentials.Credentials
|
||||
Config *config.Config
|
||||
settings *Settings
|
||||
sessions *sessions
|
||||
cache *Cache
|
||||
}
|
||||
|
||||
// NewClientWithPassword creates a new client from a password credential.
|
||||
// Set the realm to empty string to use the default realm from config.
|
||||
func NewClientWithPassword(username, realm, password string, krb5conf *config.Config, settings ...func(*Settings)) *Client {
|
||||
creds := credentials.New(username, realm)
|
||||
return &Client{
|
||||
Credentials: creds.WithPassword(password),
|
||||
Config: krb5conf,
|
||||
settings: NewSettings(settings...),
|
||||
sessions: &sessions{
|
||||
Entries: make(map[string]*session),
|
||||
},
|
||||
cache: NewCache(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewClientWithKeytab creates a new client from a keytab credential.
|
||||
func NewClientWithKeytab(username, realm string, kt *keytab.Keytab, krb5conf *config.Config, settings ...func(*Settings)) *Client {
|
||||
creds := credentials.New(username, realm)
|
||||
return &Client{
|
||||
Credentials: creds.WithKeytab(kt),
|
||||
Config: krb5conf,
|
||||
settings: NewSettings(settings...),
|
||||
sessions: &sessions{
|
||||
Entries: make(map[string]*session),
|
||||
},
|
||||
cache: NewCache(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewClientFromCCache create a client from a populated client cache.
|
||||
//
|
||||
// WARNING: A client created from CCache does not automatically renew TGTs and a failure will occur after the TGT expires.
|
||||
func NewClientFromCCache(c *credentials.CCache, krb5conf *config.Config, settings ...func(*Settings)) (*Client, error) {
|
||||
cl := &Client{
|
||||
Credentials: c.GetClientCredentials(),
|
||||
Config: krb5conf,
|
||||
settings: NewSettings(settings...),
|
||||
sessions: &sessions{
|
||||
Entries: make(map[string]*session),
|
||||
},
|
||||
cache: NewCache(),
|
||||
}
|
||||
spn := types.PrincipalName{
|
||||
NameType: nametype.KRB_NT_SRV_INST,
|
||||
NameString: []string{"krbtgt", c.DefaultPrincipal.Realm},
|
||||
}
|
||||
cred, ok := c.GetEntry(spn)
|
||||
if !ok {
|
||||
return cl, errors.New("TGT not found in CCache")
|
||||
}
|
||||
var tgt messages.Ticket
|
||||
err := tgt.Unmarshal(cred.Ticket)
|
||||
if err != nil {
|
||||
return cl, fmt.Errorf("TGT bytes in cache are not valid: %v", err)
|
||||
}
|
||||
cl.sessions.Entries[c.DefaultPrincipal.Realm] = &session{
|
||||
realm: c.DefaultPrincipal.Realm,
|
||||
authTime: cred.AuthTime,
|
||||
endTime: cred.EndTime,
|
||||
renewTill: cred.RenewTill,
|
||||
tgt: tgt,
|
||||
sessionKey: cred.Key,
|
||||
}
|
||||
for _, cred := range c.GetEntries() {
|
||||
var tkt messages.Ticket
|
||||
err = tkt.Unmarshal(cred.Ticket)
|
||||
if err != nil {
|
||||
return cl, fmt.Errorf("cache entry ticket bytes are not valid: %v", err)
|
||||
}
|
||||
cl.cache.addEntry(
|
||||
tkt,
|
||||
cred.AuthTime,
|
||||
cred.StartTime,
|
||||
cred.EndTime,
|
||||
cred.RenewTill,
|
||||
cred.Key,
|
||||
)
|
||||
}
|
||||
return cl, nil
|
||||
}
|
||||
|
||||
// Key returns the client's encryption key for the specified encryption type.
|
||||
// The key can be retrieved either from the keytab or generated from the client's password.
|
||||
// If the client has both a keytab and a password defined the keytab is favoured as the source for the key
|
||||
// A KRBError can be passed in the event the KDC returns one of type KDC_ERR_PREAUTH_REQUIRED and is required to derive
|
||||
// the key for pre-authentication from the client's password. If a KRBError is not available, pass nil to this argument.
|
||||
func (cl *Client) Key(etype etype.EType, krberr *messages.KRBError) (types.EncryptionKey, error) {
|
||||
if cl.Credentials.HasKeytab() && etype != nil {
|
||||
return cl.Credentials.Keytab().GetEncryptionKey(cl.Credentials.CName(), cl.Credentials.Domain(), 0, etype.GetETypeID())
|
||||
} else if cl.Credentials.HasPassword() {
|
||||
if krberr != nil && krberr.ErrorCode == errorcode.KDC_ERR_PREAUTH_REQUIRED {
|
||||
var pas types.PADataSequence
|
||||
err := pas.Unmarshal(krberr.EData)
|
||||
if err != nil {
|
||||
return types.EncryptionKey{}, fmt.Errorf("could not get PAData from KRBError to generate key from password: %v", err)
|
||||
}
|
||||
key, _, err := crypto.GetKeyFromPassword(cl.Credentials.Password(), krberr.CName, krberr.CRealm, etype.GetETypeID(), pas)
|
||||
return key, err
|
||||
}
|
||||
key, _, err := crypto.GetKeyFromPassword(cl.Credentials.Password(), cl.Credentials.CName(), cl.Credentials.Domain(), etype.GetETypeID(), types.PADataSequence{})
|
||||
return key, err
|
||||
}
|
||||
return types.EncryptionKey{}, errors.New("credential has neither keytab or password to generate key")
|
||||
}
|
||||
|
||||
// IsConfigured indicates if the client has the values required set.
|
||||
func (cl *Client) IsConfigured() (bool, error) {
|
||||
if cl.Credentials.UserName() == "" {
|
||||
return false, errors.New("client does not have a username")
|
||||
}
|
||||
if cl.Credentials.Domain() == "" {
|
||||
return false, errors.New("client does not have a define realm")
|
||||
}
|
||||
// Client needs to have either a password, keytab or a session already (later when loading from CCache)
|
||||
if !cl.Credentials.HasPassword() && !cl.Credentials.HasKeytab() {
|
||||
authTime, _, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
|
||||
if err != nil || authTime.IsZero() {
|
||||
return false, errors.New("client has neither a keytab nor a password set and no session")
|
||||
}
|
||||
}
|
||||
if !cl.Config.LibDefaults.DNSLookupKDC {
|
||||
for _, r := range cl.Config.Realms {
|
||||
if r.Realm == cl.Credentials.Domain() {
|
||||
if len(r.KDC) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
return false, errors.New("client krb5 config does not have any defined KDCs for the default realm")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Login the client with the KDC via an AS exchange.
|
||||
func (cl *Client) Login() error {
|
||||
if ok, err := cl.IsConfigured(); !ok {
|
||||
return err
|
||||
}
|
||||
if !cl.Credentials.HasPassword() && !cl.Credentials.HasKeytab() {
|
||||
_, endTime, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.KRBMsgError, "no user credentials available and error getting any existing session")
|
||||
}
|
||||
if time.Now().UTC().After(endTime) {
|
||||
return krberror.NewKrberror(krberror.KRBMsgError, "cannot login, no user credentials available and no valid existing session")
|
||||
}
|
||||
// no credentials but there is a session with tgt already
|
||||
return nil
|
||||
}
|
||||
ASReq, err := messages.NewASReqForTGT(cl.Credentials.Domain(), cl.Config, cl.Credentials.CName())
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.KRBMsgError, "error generating new AS_REQ")
|
||||
}
|
||||
ASRep, err := cl.ASExchange(cl.Credentials.Domain(), ASReq, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cl.addSession(ASRep.Ticket, ASRep.DecryptedEncPart)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AffirmLogin will only perform an AS exchange with the KDC if the client does not already have a TGT.
|
||||
func (cl *Client) AffirmLogin() error {
|
||||
_, endTime, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
|
||||
if err != nil || time.Now().UTC().After(endTime) {
|
||||
err := cl.Login()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get valid TGT for client's realm: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// realmLogin obtains or renews a TGT and establishes a session for the realm specified.
|
||||
func (cl *Client) realmLogin(realm string) error {
|
||||
if realm == cl.Credentials.Domain() {
|
||||
return cl.Login()
|
||||
}
|
||||
_, endTime, _, _, err := cl.sessionTimes(cl.Credentials.Domain())
|
||||
if err != nil || time.Now().UTC().After(endTime) {
|
||||
err := cl.Login()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get valid TGT for client's realm: %v", err)
|
||||
}
|
||||
}
|
||||
tgt, skey, err := cl.sessionTGT(cl.Credentials.Domain())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
spn := types.PrincipalName{
|
||||
NameType: nametype.KRB_NT_SRV_INST,
|
||||
NameString: []string{"krbtgt", realm},
|
||||
}
|
||||
|
||||
_, tgsRep, err := cl.TGSREQGenerateAndExchange(spn, cl.Credentials.Domain(), tgt, skey, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cl.addSession(tgsRep.Ticket, tgsRep.DecryptedEncPart)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy stops the auto-renewal of all sessions and removes the sessions and cache entries from the client.
|
||||
func (cl *Client) Destroy() {
|
||||
creds := credentials.New("", "")
|
||||
cl.sessions.destroy()
|
||||
cl.cache.clear()
|
||||
cl.Credentials = creds
|
||||
cl.Log("client destroyed")
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/errorcode"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/messages"
|
||||
)
|
||||
|
||||
// SendToKDC performs network actions to send data to the KDC.
|
||||
func (cl *Client) sendToKDC(b []byte, realm string) ([]byte, error) {
|
||||
var rb []byte
|
||||
if cl.Config.LibDefaults.UDPPreferenceLimit == 1 {
|
||||
//1 means we should always use TCP
|
||||
rb, errtcp := cl.sendKDCTCP(realm, b)
|
||||
if errtcp != nil {
|
||||
if e, ok := errtcp.(messages.KRBError); ok {
|
||||
return rb, e
|
||||
}
|
||||
return rb, fmt.Errorf("communication error with KDC via TCP: %v", errtcp)
|
||||
}
|
||||
return rb, nil
|
||||
}
|
||||
if len(b) <= cl.Config.LibDefaults.UDPPreferenceLimit {
|
||||
//Try UDP first, TCP second
|
||||
rb, errudp := cl.sendKDCUDP(realm, b)
|
||||
if errudp != nil {
|
||||
if e, ok := errudp.(messages.KRBError); ok && e.ErrorCode != errorcode.KRB_ERR_RESPONSE_TOO_BIG {
|
||||
// Got a KRBError from KDC
|
||||
// If this is not a KRB_ERR_RESPONSE_TOO_BIG we will return immediately otherwise will try TCP.
|
||||
return rb, e
|
||||
}
|
||||
// Try TCP
|
||||
r, errtcp := cl.sendKDCTCP(realm, b)
|
||||
if errtcp != nil {
|
||||
if e, ok := errtcp.(messages.KRBError); ok {
|
||||
// Got a KRBError
|
||||
return r, e
|
||||
}
|
||||
return r, fmt.Errorf("failed to communicate with KDC. Attempts made with UDP (%v) and then TCP (%v)", errudp, errtcp)
|
||||
}
|
||||
rb = r
|
||||
}
|
||||
return rb, nil
|
||||
}
|
||||
//Try TCP first, UDP second
|
||||
rb, errtcp := cl.sendKDCTCP(realm, b)
|
||||
if errtcp != nil {
|
||||
if e, ok := errtcp.(messages.KRBError); ok {
|
||||
// Got a KRBError from KDC so returning and not trying UDP.
|
||||
return rb, e
|
||||
}
|
||||
rb, errudp := cl.sendKDCUDP(realm, b)
|
||||
if errudp != nil {
|
||||
if e, ok := errudp.(messages.KRBError); ok {
|
||||
// Got a KRBError
|
||||
return rb, e
|
||||
}
|
||||
return rb, fmt.Errorf("failed to communicate with KDC. Attempts made with TCP (%v) and then UDP (%v)", errtcp, errudp)
|
||||
}
|
||||
}
|
||||
return rb, nil
|
||||
}
|
||||
|
||||
// dialKDCTCP establishes a UDP connection to a KDC.
|
||||
func dialKDCUDP(count int, kdcs map[int]string) (*net.UDPConn, error) {
|
||||
i := 1
|
||||
for i <= count {
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", kdcs[i])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error resolving KDC address: %v", err)
|
||||
}
|
||||
|
||||
conn, err := net.DialTimeout("udp", udpAddr.String(), 5*time.Second)
|
||||
if err == nil {
|
||||
if err := conn.SetDeadline(time.Now().Add(5 * time.Second)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// conn is guaranteed to be a UDPConn
|
||||
return conn.(*net.UDPConn), nil
|
||||
}
|
||||
i++
|
||||
}
|
||||
return nil, errors.New("error in getting a UDP connection to any of the KDCs")
|
||||
}
|
||||
|
||||
// dialKDCTCP establishes a TCP connection to a KDC.
|
||||
func dialKDCTCP(count int, kdcs map[int]string) (*net.TCPConn, error) {
|
||||
i := 1
|
||||
for i <= count {
|
||||
tcpAddr, err := net.ResolveTCPAddr("tcp", kdcs[i])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error resolving KDC address: %v", err)
|
||||
}
|
||||
|
||||
conn, err := net.DialTimeout("tcp", tcpAddr.String(), 5*time.Second)
|
||||
if err == nil {
|
||||
if err := conn.SetDeadline(time.Now().Add(5 * time.Second)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// conn is guaranteed to be a TCPConn
|
||||
return conn.(*net.TCPConn), nil
|
||||
}
|
||||
i++
|
||||
}
|
||||
return nil, errors.New("error in getting a TCP connection to any of the KDCs")
|
||||
}
|
||||
|
||||
// sendKDCUDP sends bytes to the KDC via UDP.
|
||||
func (cl *Client) sendKDCUDP(realm string, b []byte) ([]byte, error) {
|
||||
var r []byte
|
||||
count, kdcs, err := cl.Config.GetKDCs(realm, false)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
conn, err := dialKDCUDP(count, kdcs)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
r, err = cl.sendUDP(conn, b)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
return checkForKRBError(r)
|
||||
}
|
||||
|
||||
// sendKDCTCP sends bytes to the KDC via TCP.
|
||||
func (cl *Client) sendKDCTCP(realm string, b []byte) ([]byte, error) {
|
||||
var r []byte
|
||||
count, kdcs, err := cl.Config.GetKDCs(realm, true)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
conn, err := dialKDCTCP(count, kdcs)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
rb, err := cl.sendTCP(conn, b)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
return checkForKRBError(rb)
|
||||
}
|
||||
|
||||
// sendUDP sends bytes to connection over UDP.
|
||||
func (cl *Client) sendUDP(conn *net.UDPConn, b []byte) ([]byte, error) {
|
||||
var r []byte
|
||||
defer conn.Close()
|
||||
_, err := conn.Write(b)
|
||||
if err != nil {
|
||||
return r, fmt.Errorf("error sending to (%s): %v", conn.RemoteAddr().String(), err)
|
||||
}
|
||||
udpbuf := make([]byte, 4096)
|
||||
n, _, err := conn.ReadFrom(udpbuf)
|
||||
r = udpbuf[:n]
|
||||
if err != nil {
|
||||
return r, fmt.Errorf("sending over UDP failed to %s: %v", conn.RemoteAddr().String(), err)
|
||||
}
|
||||
if len(r) < 1 {
|
||||
return r, fmt.Errorf("no response data from %s", conn.RemoteAddr().String())
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// sendTCP sends bytes to connection over TCP.
|
||||
func (cl *Client) sendTCP(conn *net.TCPConn, b []byte) ([]byte, error) {
|
||||
defer conn.Close()
|
||||
var r []byte
|
||||
/*
|
||||
RFC https://tools.ietf.org/html/rfc4120#section-7.2.2
|
||||
Each request (KRB_KDC_REQ) and response (KRB_KDC_REP or KRB_ERROR)
|
||||
sent over the TCP stream is preceded by the length of the request as
|
||||
4 octets in network byte order. The high bit of the length is
|
||||
reserved for future expansion and MUST currently be set to zero. If
|
||||
a KDC that does not understand how to interpret a set high bit of the
|
||||
length encoding receives a request with the high order bit of the
|
||||
length set, it MUST return a KRB-ERROR message with the error
|
||||
KRB_ERR_FIELD_TOOLONG and MUST close the TCP stream.
|
||||
NB: network byte order == big endian
|
||||
*/
|
||||
var buf bytes.Buffer
|
||||
err := binary.Write(&buf, binary.BigEndian, uint32(len(b)))
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
b = append(buf.Bytes(), b...)
|
||||
|
||||
_, err = conn.Write(b)
|
||||
if err != nil {
|
||||
return r, fmt.Errorf("error sending to KDC (%s): %v", conn.RemoteAddr().String(), err)
|
||||
}
|
||||
|
||||
sh := make([]byte, 4, 4)
|
||||
_, err = conn.Read(sh)
|
||||
if err != nil {
|
||||
return r, fmt.Errorf("error reading response size header: %v", err)
|
||||
}
|
||||
s := binary.BigEndian.Uint32(sh)
|
||||
|
||||
rb := make([]byte, s, s)
|
||||
_, err = io.ReadFull(conn, rb)
|
||||
if err != nil {
|
||||
return r, fmt.Errorf("error reading response: %v", err)
|
||||
}
|
||||
if len(rb) < 1 {
|
||||
return r, fmt.Errorf("no response data from KDC %s", conn.RemoteAddr().String())
|
||||
}
|
||||
return rb, nil
|
||||
}
|
||||
|
||||
// checkForKRBError checks if the response bytes from the KDC are a KRBError.
|
||||
func checkForKRBError(b []byte) ([]byte, error) {
|
||||
var KRBErr messages.KRBError
|
||||
if err := KRBErr.Unmarshal(b); err == nil {
|
||||
return b, KRBErr
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"gopkg.in/jcmturner/gokrb5.v7/kadmin"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/messages"
|
||||
)
|
||||
|
||||
// Kpasswd server response codes.
|
||||
const (
|
||||
KRB5_KPASSWD_SUCCESS = 0
|
||||
KRB5_KPASSWD_MALFORMED = 1
|
||||
KRB5_KPASSWD_HARDERROR = 2
|
||||
KRB5_KPASSWD_AUTHERROR = 3
|
||||
KRB5_KPASSWD_SOFTERROR = 4
|
||||
KRB5_KPASSWD_ACCESSDENIED = 5
|
||||
KRB5_KPASSWD_BAD_VERSION = 6
|
||||
KRB5_KPASSWD_INITIAL_FLAG_NEEDED = 7
|
||||
)
|
||||
|
||||
// ChangePasswd changes the password of the client to the value provided.
|
||||
func (cl *Client) ChangePasswd(newPasswd string) (bool, error) {
|
||||
ASReq, err := messages.NewASReqForChgPasswd(cl.Credentials.Domain(), cl.Config, cl.Credentials.CName())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ASRep, err := cl.ASExchange(cl.Credentials.Domain(), ASReq, 0)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
msg, key, err := kadmin.ChangePasswdMsg(cl.Credentials.CName(), cl.Credentials.Domain(), newPasswd, ASRep.Ticket, ASRep.DecryptedEncPart.Key)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
r, err := cl.sendToKPasswd(msg)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = r.Decrypt(key)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if r.ResultCode != KRB5_KPASSWD_SUCCESS {
|
||||
return false, fmt.Errorf("error response from kadmin: code: %d; result: %s; krberror: %v", r.ResultCode, r.Result, r.KRBError)
|
||||
}
|
||||
cl.Credentials.WithPassword(newPasswd)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (cl *Client) sendToKPasswd(msg kadmin.Request) (r kadmin.Reply, err error) {
|
||||
_, kps, err := cl.Config.GetKpasswdServers(cl.Credentials.Domain(), true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
addr := kps[1]
|
||||
b, err := msg.Marshal()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(b) <= cl.Config.LibDefaults.UDPPreferenceLimit {
|
||||
return cl.sendKPasswdUDP(b, addr)
|
||||
}
|
||||
return cl.sendKPasswdTCP(b, addr)
|
||||
}
|
||||
|
||||
func (cl *Client) sendKPasswdTCP(b []byte, kadmindAddr string) (r kadmin.Reply, err error) {
|
||||
tcpAddr, err := net.ResolveTCPAddr("tcp", kadmindAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
conn, err := net.DialTCP("tcp", nil, tcpAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rb, err := cl.sendTCP(conn, b)
|
||||
err = r.Unmarshal(rb)
|
||||
return
|
||||
}
|
||||
|
||||
func (cl *Client) sendKPasswdUDP(b []byte, kadmindAddr string) (r kadmin.Reply, err error) {
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", kadmindAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
conn, err := net.DialUDP("udp", nil, udpAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rb, err := cl.sendUDP(conn, b)
|
||||
err = r.Unmarshal(rb)
|
||||
return
|
||||
}
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/nametype"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/krberror"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/messages"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/types"
|
||||
)
|
||||
|
||||
// sessions hold TGTs and are keyed on the realm name
|
||||
type sessions struct {
|
||||
Entries map[string]*session
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
// destroy erases all sessions
|
||||
func (s *sessions) destroy() {
|
||||
s.mux.Lock()
|
||||
defer s.mux.Unlock()
|
||||
for k, e := range s.Entries {
|
||||
e.destroy()
|
||||
delete(s.Entries, k)
|
||||
}
|
||||
}
|
||||
|
||||
// update replaces a session with the one provided or adds it as a new one
|
||||
func (s *sessions) update(sess *session) {
|
||||
s.mux.Lock()
|
||||
defer s.mux.Unlock()
|
||||
// if a session already exists for this, cancel its auto renew.
|
||||
if i, ok := s.Entries[sess.realm]; ok {
|
||||
if i != sess {
|
||||
// Session in the sessions cache is not the same as one provided.
|
||||
// Cancel the one in the cache and add this one.
|
||||
i.mux.Lock()
|
||||
defer i.mux.Unlock()
|
||||
i.cancel <- true
|
||||
s.Entries[sess.realm] = sess
|
||||
return
|
||||
}
|
||||
}
|
||||
// No session for this realm was found so just add it
|
||||
s.Entries[sess.realm] = sess
|
||||
}
|
||||
|
||||
// get returns the session for the realm specified
|
||||
func (s *sessions) get(realm string) (*session, bool) {
|
||||
s.mux.RLock()
|
||||
defer s.mux.RUnlock()
|
||||
sess, ok := s.Entries[realm]
|
||||
return sess, ok
|
||||
}
|
||||
|
||||
// session holds the TGT details for a realm
|
||||
type session struct {
|
||||
realm string
|
||||
authTime time.Time
|
||||
endTime time.Time
|
||||
renewTill time.Time
|
||||
tgt messages.Ticket
|
||||
sessionKey types.EncryptionKey
|
||||
sessionKeyExpiration time.Time
|
||||
cancel chan bool
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
// AddSession adds a session for a realm with a TGT to the client's session cache.
|
||||
// A goroutine is started to automatically renew the TGT before expiry.
|
||||
func (cl *Client) addSession(tgt messages.Ticket, dep messages.EncKDCRepPart) {
|
||||
if strings.ToLower(tgt.SName.NameString[0]) != "krbtgt" {
|
||||
// Not a TGT
|
||||
return
|
||||
}
|
||||
realm := tgt.SName.NameString[len(tgt.SName.NameString)-1]
|
||||
s := &session{
|
||||
realm: realm,
|
||||
authTime: dep.AuthTime,
|
||||
endTime: dep.EndTime,
|
||||
renewTill: dep.RenewTill,
|
||||
tgt: tgt,
|
||||
sessionKey: dep.Key,
|
||||
sessionKeyExpiration: dep.KeyExpiration,
|
||||
}
|
||||
cl.sessions.update(s)
|
||||
cl.enableAutoSessionRenewal(s)
|
||||
cl.Log("TGT session added for %s (EndTime: %v)", realm, dep.EndTime)
|
||||
}
|
||||
|
||||
// update overwrites the session details with those from the TGT and decrypted encPart
|
||||
func (s *session) update(tgt messages.Ticket, dep messages.EncKDCRepPart) {
|
||||
s.mux.Lock()
|
||||
defer s.mux.Unlock()
|
||||
s.authTime = dep.AuthTime
|
||||
s.endTime = dep.EndTime
|
||||
s.renewTill = dep.RenewTill
|
||||
s.tgt = tgt
|
||||
s.sessionKey = dep.Key
|
||||
s.sessionKeyExpiration = dep.KeyExpiration
|
||||
}
|
||||
|
||||
// destroy will cancel any auto renewal of the session and set the expiration times to the current time
|
||||
func (s *session) destroy() {
|
||||
s.mux.Lock()
|
||||
defer s.mux.Unlock()
|
||||
if s.cancel != nil {
|
||||
s.cancel <- true
|
||||
}
|
||||
s.endTime = time.Now().UTC()
|
||||
s.renewTill = s.endTime
|
||||
s.sessionKeyExpiration = s.endTime
|
||||
}
|
||||
|
||||
// valid informs if the TGT is still within the valid time window
|
||||
func (s *session) valid() bool {
|
||||
s.mux.RLock()
|
||||
defer s.mux.RUnlock()
|
||||
t := time.Now().UTC()
|
||||
if t.Before(s.endTime) && s.authTime.Before(t) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// tgtDetails is a thread safe way to get the session's realm, TGT and session key values
|
||||
func (s *session) tgtDetails() (string, messages.Ticket, types.EncryptionKey) {
|
||||
s.mux.RLock()
|
||||
defer s.mux.RUnlock()
|
||||
return s.realm, s.tgt, s.sessionKey
|
||||
}
|
||||
|
||||
// timeDetails is a thread safe way to get the session's validity time values
|
||||
func (s *session) timeDetails() (string, time.Time, time.Time, time.Time, time.Time) {
|
||||
s.mux.RLock()
|
||||
defer s.mux.RUnlock()
|
||||
return s.realm, s.authTime, s.endTime, s.renewTill, s.sessionKeyExpiration
|
||||
}
|
||||
|
||||
// enableAutoSessionRenewal turns on the automatic renewal for the client's TGT session.
|
||||
func (cl *Client) enableAutoSessionRenewal(s *session) {
|
||||
var timer *time.Timer
|
||||
s.mux.Lock()
|
||||
s.cancel = make(chan bool, 1)
|
||||
s.mux.Unlock()
|
||||
go func(s *session) {
|
||||
for {
|
||||
s.mux.RLock()
|
||||
w := (s.endTime.Sub(time.Now().UTC()) * 5) / 6
|
||||
s.mux.RUnlock()
|
||||
if w < 0 {
|
||||
return
|
||||
}
|
||||
timer = time.NewTimer(w)
|
||||
select {
|
||||
case <-timer.C:
|
||||
renewal, err := cl.refreshSession(s)
|
||||
if err != nil {
|
||||
cl.Log("error refreshing session: %v", err)
|
||||
}
|
||||
if !renewal && err == nil {
|
||||
// end this goroutine as there will have been a new login and new auto renewal goroutine created.
|
||||
return
|
||||
}
|
||||
case <-s.cancel:
|
||||
// cancel has been called. Stop the timer and exit.
|
||||
timer.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}(s)
|
||||
}
|
||||
|
||||
// renewTGT renews the client's TGT session.
|
||||
func (cl *Client) renewTGT(s *session) error {
|
||||
realm, tgt, skey := s.tgtDetails()
|
||||
spn := types.PrincipalName{
|
||||
NameType: nametype.KRB_NT_SRV_INST,
|
||||
NameString: []string{"krbtgt", realm},
|
||||
}
|
||||
_, tgsRep, err := cl.TGSREQGenerateAndExchange(spn, cl.Credentials.Domain(), tgt, skey, true)
|
||||
if err != nil {
|
||||
return krberror.Errorf(err, krberror.KRBMsgError, "error renewing TGT for %s", realm)
|
||||
}
|
||||
s.update(tgsRep.Ticket, tgsRep.DecryptedEncPart)
|
||||
cl.sessions.update(s)
|
||||
cl.Log("TGT session renewed for %s (EndTime: %v)", realm, tgsRep.DecryptedEncPart.EndTime)
|
||||
return nil
|
||||
}
|
||||
|
||||
// refreshSession updates either through renewal or creating a new login.
|
||||
// The boolean indicates if the update was a renewal.
|
||||
func (cl *Client) refreshSession(s *session) (bool, error) {
|
||||
s.mux.RLock()
|
||||
realm := s.realm
|
||||
renewTill := s.renewTill
|
||||
s.mux.RUnlock()
|
||||
cl.Log("refreshing TGT session for %s", realm)
|
||||
if time.Now().UTC().Before(renewTill) {
|
||||
err := cl.renewTGT(s)
|
||||
return true, err
|
||||
}
|
||||
err := cl.realmLogin(realm)
|
||||
return false, err
|
||||
}
|
||||
|
||||
// ensureValidSession makes sure there is a valid session for the realm
|
||||
func (cl *Client) ensureValidSession(realm string) error {
|
||||
s, ok := cl.sessions.get(realm)
|
||||
if ok {
|
||||
s.mux.RLock()
|
||||
d := s.endTime.Sub(s.authTime) / 6
|
||||
if s.endTime.Sub(time.Now().UTC()) > d {
|
||||
s.mux.RUnlock()
|
||||
return nil
|
||||
}
|
||||
s.mux.RUnlock()
|
||||
_, err := cl.refreshSession(s)
|
||||
return err
|
||||
}
|
||||
return cl.realmLogin(realm)
|
||||
}
|
||||
|
||||
// sessionTGTDetails is a thread safe way to get the TGT and session key values for a realm
|
||||
func (cl *Client) sessionTGT(realm string) (tgt messages.Ticket, sessionKey types.EncryptionKey, err error) {
|
||||
err = cl.ensureValidSession(realm)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s, ok := cl.sessions.get(realm)
|
||||
if !ok {
|
||||
err = fmt.Errorf("could not find TGT session for %s", realm)
|
||||
return
|
||||
}
|
||||
_, tgt, sessionKey = s.tgtDetails()
|
||||
return
|
||||
}
|
||||
|
||||
func (cl *Client) sessionTimes(realm string) (authTime, endTime, renewTime, sessionExp time.Time, err error) {
|
||||
s, ok := cl.sessions.get(realm)
|
||||
if !ok {
|
||||
err = fmt.Errorf("could not find TGT session for %s", realm)
|
||||
return
|
||||
}
|
||||
_, authTime, endTime, renewTime, sessionExp = s.timeDetails()
|
||||
return
|
||||
}
|
||||
|
||||
// spnRealm resolves the realm name of a service principal name
|
||||
func (cl *Client) spnRealm(spn types.PrincipalName) string {
|
||||
return cl.Config.ResolveRealm(spn.NameString[len(spn.NameString)-1])
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package client
|
||||
|
||||
import "log"
|
||||
|
||||
// Settings holds optional client settings.
|
||||
type Settings struct {
|
||||
disablePAFXFast bool
|
||||
assumePreAuthentication bool
|
||||
preAuthEType int32
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// NewSettings creates a new client settings struct.
|
||||
func NewSettings(settings ...func(*Settings)) *Settings {
|
||||
s := new(Settings)
|
||||
for _, set := range settings {
|
||||
set(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// DisablePAFXFAST used to configure the client to not use PA_FX_FAST.
|
||||
//
|
||||
// s := NewSettings(DisablePAFXFAST(true))
|
||||
func DisablePAFXFAST(b bool) func(*Settings) {
|
||||
return func(s *Settings) {
|
||||
s.disablePAFXFast = b
|
||||
}
|
||||
}
|
||||
|
||||
// DisablePAFXFAST indicates is the client should disable the use of PA_FX_FAST.
|
||||
func (s *Settings) DisablePAFXFAST() bool {
|
||||
return s.disablePAFXFast
|
||||
}
|
||||
|
||||
// AssumePreAuthentication used to configure the client to assume pre-authentication is required.
|
||||
//
|
||||
// s := NewSettings(AssumePreAuthentication(true))
|
||||
func AssumePreAuthentication(b bool) func(*Settings) {
|
||||
return func(s *Settings) {
|
||||
s.assumePreAuthentication = b
|
||||
}
|
||||
}
|
||||
|
||||
// AssumePreAuthentication indicates if the client should proactively assume using pre-authentication.
|
||||
func (s *Settings) AssumePreAuthentication() bool {
|
||||
return s.assumePreAuthentication
|
||||
}
|
||||
|
||||
// Logger used to configure client with a logger.
|
||||
//
|
||||
// s := NewSettings(kt, Logger(l))
|
||||
func Logger(l *log.Logger) func(*Settings) {
|
||||
return func(s *Settings) {
|
||||
s.logger = l
|
||||
}
|
||||
}
|
||||
|
||||
// Logger returns the client logger instance.
|
||||
func (s *Settings) Logger() *log.Logger {
|
||||
return s.logger
|
||||
}
|
||||
|
||||
// Log will write to the service's logger if it is configured.
|
||||
func (cl *Client) Log(format string, v ...interface{}) {
|
||||
if cl.settings.Logger() != nil {
|
||||
cl.settings.Logger().Printf(format, v...)
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package config
|
||||
|
||||
import "fmt"
|
||||
|
||||
// UnsupportedDirective error.
|
||||
type UnsupportedDirective struct {
|
||||
text string
|
||||
}
|
||||
|
||||
// Error implements the error interface for unsupported directives.
|
||||
func (e UnsupportedDirective) Error() string {
|
||||
return e.text
|
||||
}
|
||||
|
||||
// Invalid config error.
|
||||
type Invalid struct {
|
||||
text string
|
||||
}
|
||||
|
||||
// Error implements the error interface for invalid config error.
|
||||
func (e Invalid) Error() string {
|
||||
return e.text
|
||||
}
|
||||
|
||||
// InvalidErrorf creates a new Invalid error.
|
||||
func InvalidErrorf(format string, a ...interface{}) Invalid {
|
||||
return Invalid{
|
||||
text: fmt.Sprintf("invalid krb5 config "+format, a...),
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/jcmturner/dnsutils.v1"
|
||||
)
|
||||
|
||||
// GetKDCs returns the count of KDCs available and a map of KDC host names keyed on preference order.
|
||||
func (c *Config) GetKDCs(realm string, tcp bool) (int, map[int]string, error) {
|
||||
if realm == "" {
|
||||
realm = c.LibDefaults.DefaultRealm
|
||||
}
|
||||
kdcs := make(map[int]string)
|
||||
var count int
|
||||
|
||||
// Get the KDCs from the krb5.conf.
|
||||
var ks []string
|
||||
for _, r := range c.Realms {
|
||||
if r.Realm != realm {
|
||||
continue
|
||||
}
|
||||
ks = r.KDC
|
||||
}
|
||||
count = len(ks)
|
||||
|
||||
if count > 0 {
|
||||
// Order the kdcs randomly for preference.
|
||||
kdcs = randServOrder(ks)
|
||||
return count, kdcs, nil
|
||||
}
|
||||
|
||||
if !c.LibDefaults.DNSLookupKDC {
|
||||
return count, kdcs, fmt.Errorf("no KDCs defined in configuration for realm %s", realm)
|
||||
}
|
||||
|
||||
// Use DNS to resolve kerberos SRV records.
|
||||
proto := "udp"
|
||||
if tcp {
|
||||
proto = "tcp"
|
||||
}
|
||||
index, addrs, err := dnsutils.OrderedSRV("kerberos", proto, realm)
|
||||
if err != nil {
|
||||
return count, kdcs, err
|
||||
}
|
||||
if len(addrs) < 1 {
|
||||
return count, kdcs, fmt.Errorf("no KDC SRV records found for realm %s", realm)
|
||||
}
|
||||
count = index
|
||||
for k, v := range addrs {
|
||||
kdcs[k] = strings.TrimRight(v.Target, ".") + ":" + strconv.Itoa(int(v.Port))
|
||||
}
|
||||
return count, kdcs, nil
|
||||
}
|
||||
|
||||
// GetKpasswdServers returns the count of kpasswd servers available and a map of kpasswd host names keyed on preference order.
|
||||
// https://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html#realms - see kpasswd_server section
|
||||
func (c *Config) GetKpasswdServers(realm string, tcp bool) (int, map[int]string, error) {
|
||||
kdcs := make(map[int]string)
|
||||
var count int
|
||||
|
||||
// Use DNS to resolve kerberos SRV records if configured to do so in krb5.conf.
|
||||
if c.LibDefaults.DNSLookupKDC {
|
||||
proto := "udp"
|
||||
if tcp {
|
||||
proto = "tcp"
|
||||
}
|
||||
c, addrs, err := dnsutils.OrderedSRV("kpasswd", proto, realm)
|
||||
if err != nil {
|
||||
return count, kdcs, err
|
||||
}
|
||||
if c < 1 {
|
||||
c, addrs, err = dnsutils.OrderedSRV("kerberos-adm", proto, realm)
|
||||
if err != nil {
|
||||
return count, kdcs, err
|
||||
}
|
||||
}
|
||||
if len(addrs) < 1 {
|
||||
return count, kdcs, fmt.Errorf("no kpasswd or kadmin SRV records found for realm %s", realm)
|
||||
}
|
||||
count = c
|
||||
for k, v := range addrs {
|
||||
kdcs[k] = strings.TrimRight(v.Target, ".") + ":" + strconv.Itoa(int(v.Port))
|
||||
}
|
||||
} else {
|
||||
// Get the KDCs from the krb5.conf an order them randomly for preference.
|
||||
var ks []string
|
||||
var ka []string
|
||||
for _, r := range c.Realms {
|
||||
if r.Realm == realm {
|
||||
ks = r.KPasswdServer
|
||||
ka = r.AdminServer
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(ks) < 1 {
|
||||
for _, k := range ka {
|
||||
h, _, err := net.SplitHostPort(k)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ks = append(ks, h+":464")
|
||||
}
|
||||
}
|
||||
count = len(ks)
|
||||
if count < 1 {
|
||||
return count, kdcs, fmt.Errorf("no kpasswd or kadmin defined in configuration for realm %s", realm)
|
||||
}
|
||||
kdcs = randServOrder(ks)
|
||||
}
|
||||
return count, kdcs, nil
|
||||
}
|
||||
|
||||
func randServOrder(ks []string) map[int]string {
|
||||
kdcs := make(map[int]string)
|
||||
count := len(ks)
|
||||
i := 1
|
||||
if count > 1 {
|
||||
l := len(ks)
|
||||
for l > 0 {
|
||||
ri := rand.Intn(l)
|
||||
kdcs[i] = ks[ri]
|
||||
if l > 1 {
|
||||
// Remove the entry from the source slice by swapping with the last entry and truncating
|
||||
ks[len(ks)-1], ks[ri] = ks[ri], ks[len(ks)-1]
|
||||
ks = ks[:len(ks)-1]
|
||||
l = len(ks)
|
||||
} else {
|
||||
l = 0
|
||||
}
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
kdcs[i] = ks[0]
|
||||
}
|
||||
return kdcs
|
||||
}
|
||||
+726
@@ -0,0 +1,726 @@
|
||||
// Package config implements KRB5 client and service configuration as described at https://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html
|
||||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/user"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jcmturner/gofork/encoding/asn1"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/etypeID"
|
||||
)
|
||||
|
||||
// Config represents the KRB5 configuration.
|
||||
type Config struct {
|
||||
LibDefaults LibDefaults
|
||||
Realms []Realm
|
||||
DomainRealm DomainRealm
|
||||
//CaPaths
|
||||
//AppDefaults
|
||||
//Plugins
|
||||
}
|
||||
|
||||
// WeakETypeList is a list of encryption types that have been deemed weak.
|
||||
const WeakETypeList = "des-cbc-crc des-cbc-md4 des-cbc-md5 des-cbc-raw des3-cbc-raw des-hmac-sha1 arcfour-hmac-exp rc4-hmac-exp arcfour-hmac-md5-exp des"
|
||||
|
||||
// NewConfig creates a new config struct instance.
|
||||
func NewConfig() *Config {
|
||||
d := make(DomainRealm)
|
||||
return &Config{
|
||||
LibDefaults: newLibDefaults(),
|
||||
DomainRealm: d,
|
||||
}
|
||||
}
|
||||
|
||||
// LibDefaults represents the [libdefaults] section of the configuration.
|
||||
type LibDefaults struct {
|
||||
AllowWeakCrypto bool //default false
|
||||
// ap_req_checksum_type int //unlikely to support this
|
||||
Canonicalize bool //default false
|
||||
CCacheType int //default is 4. unlikely to implement older
|
||||
Clockskew time.Duration //max allowed skew in seconds, default 300
|
||||
//Default_ccache_name string // default /tmp/krb5cc_%{uid} //Not implementing as will hold in memory
|
||||
DefaultClientKeytabName string //default /usr/local/var/krb5/user/%{euid}/client.keytab
|
||||
DefaultKeytabName string //default /etc/krb5.keytab
|
||||
DefaultRealm string
|
||||
DefaultTGSEnctypes []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
|
||||
DefaultTktEnctypes []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
|
||||
DefaultTGSEnctypeIDs []int32 //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
|
||||
DefaultTktEnctypeIDs []int32 //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
|
||||
DNSCanonicalizeHostname bool //default true
|
||||
DNSLookupKDC bool //default false
|
||||
DNSLookupRealm bool
|
||||
ExtraAddresses []net.IP //Not implementing yet
|
||||
Forwardable bool //default false
|
||||
IgnoreAcceptorHostname bool //default false
|
||||
K5LoginAuthoritative bool //default false
|
||||
K5LoginDirectory string //default user's home directory. Must be owned by the user or root
|
||||
KDCDefaultOptions asn1.BitString //default 0x00000010 (KDC_OPT_RENEWABLE_OK)
|
||||
KDCTimeSync int //default 1
|
||||
//kdc_req_checksum_type int //unlikely to implement as for very old KDCs
|
||||
NoAddresses bool //default true
|
||||
PermittedEnctypes []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
|
||||
PermittedEnctypeIDs []int32
|
||||
//plugin_base_dir string //not supporting plugins
|
||||
PreferredPreauthTypes []int //default “17, 16, 15, 14”, which forces libkrb5 to attempt to use PKINIT if it is supported
|
||||
Proxiable bool //default false
|
||||
RDNS bool //default true
|
||||
RealmTryDomains int //default -1
|
||||
RenewLifetime time.Duration //default 0
|
||||
SafeChecksumType int //default 8
|
||||
TicketLifetime time.Duration //default 1 day
|
||||
UDPPreferenceLimit int // 1 means to always use tcp. MIT krb5 has a default value of 1465, and it prevents user setting more than 32700.
|
||||
VerifyAPReqNofail bool //default false
|
||||
}
|
||||
|
||||
// Create a new LibDefaults struct.
|
||||
func newLibDefaults() LibDefaults {
|
||||
uid := "0"
|
||||
var hdir string
|
||||
usr, _ := user.Current()
|
||||
if usr != nil {
|
||||
uid = usr.Uid
|
||||
hdir = usr.HomeDir
|
||||
}
|
||||
opts := asn1.BitString{}
|
||||
opts.Bytes, _ = hex.DecodeString("00000010")
|
||||
opts.BitLength = len(opts.Bytes) * 8
|
||||
return LibDefaults{
|
||||
CCacheType: 4,
|
||||
Clockskew: time.Duration(300) * time.Second,
|
||||
DefaultClientKeytabName: fmt.Sprintf("/usr/local/var/krb5/user/%s/client.keytab", uid),
|
||||
DefaultKeytabName: "/etc/krb5.keytab",
|
||||
DefaultTGSEnctypes: []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96", "des3-cbc-sha1", "arcfour-hmac-md5", "camellia256-cts-cmac", "camellia128-cts-cmac", "des-cbc-crc", "des-cbc-md5", "des-cbc-md4"},
|
||||
DefaultTktEnctypes: []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96", "des3-cbc-sha1", "arcfour-hmac-md5", "camellia256-cts-cmac", "camellia128-cts-cmac", "des-cbc-crc", "des-cbc-md5", "des-cbc-md4"},
|
||||
DNSCanonicalizeHostname: true,
|
||||
K5LoginDirectory: hdir,
|
||||
KDCDefaultOptions: opts,
|
||||
KDCTimeSync: 1,
|
||||
NoAddresses: true,
|
||||
PermittedEnctypes: []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96", "des3-cbc-sha1", "arcfour-hmac-md5", "camellia256-cts-cmac", "camellia128-cts-cmac", "des-cbc-crc", "des-cbc-md5", "des-cbc-md4"},
|
||||
RDNS: true,
|
||||
RealmTryDomains: -1,
|
||||
SafeChecksumType: 8,
|
||||
TicketLifetime: time.Duration(24) * time.Hour,
|
||||
UDPPreferenceLimit: 1465,
|
||||
PreferredPreauthTypes: []int{17, 16, 15, 14},
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the lines of the [libdefaults] section of the configuration into the LibDefaults struct.
|
||||
func (l *LibDefaults) parseLines(lines []string) error {
|
||||
for _, line := range lines {
|
||||
//Remove comments after the values
|
||||
if idx := strings.IndexAny(line, "#;"); idx != -1 {
|
||||
line = line[:idx]
|
||||
}
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(line, "=") {
|
||||
return InvalidErrorf("libdefaults section line (%s)", line)
|
||||
}
|
||||
|
||||
p := strings.Split(line, "=")
|
||||
key := strings.TrimSpace(strings.ToLower(p[0]))
|
||||
switch key {
|
||||
case "allow_weak_crypto":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.AllowWeakCrypto = v
|
||||
case "canonicalize":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.Canonicalize = v
|
||||
case "ccache_type":
|
||||
p[1] = strings.TrimSpace(p[1])
|
||||
v, err := strconv.ParseUint(p[1], 10, 32)
|
||||
if err != nil || v < 0 || v > 4 {
|
||||
return InvalidErrorf("libdefaults section line (%s)", line)
|
||||
}
|
||||
l.CCacheType = int(v)
|
||||
case "clockskew":
|
||||
d, err := parseDuration(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.Clockskew = d
|
||||
case "default_client_keytab_name":
|
||||
l.DefaultClientKeytabName = strings.TrimSpace(p[1])
|
||||
case "default_keytab_name":
|
||||
l.DefaultKeytabName = strings.TrimSpace(p[1])
|
||||
case "default_realm":
|
||||
l.DefaultRealm = strings.TrimSpace(p[1])
|
||||
case "default_tgs_enctypes":
|
||||
l.DefaultTGSEnctypes = strings.Fields(p[1])
|
||||
case "default_tkt_enctypes":
|
||||
l.DefaultTktEnctypes = strings.Fields(p[1])
|
||||
case "dns_canonicalize_hostname":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.DNSCanonicalizeHostname = v
|
||||
case "dns_lookup_kdc":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.DNSLookupKDC = v
|
||||
case "dns_lookup_realm":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.DNSLookupRealm = v
|
||||
case "extra_addresses":
|
||||
ipStr := strings.TrimSpace(p[1])
|
||||
for _, ip := range strings.Split(ipStr, ",") {
|
||||
if eip := net.ParseIP(ip); eip != nil {
|
||||
l.ExtraAddresses = append(l.ExtraAddresses, eip)
|
||||
}
|
||||
}
|
||||
case "forwardable":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.Forwardable = v
|
||||
case "ignore_acceptor_hostname":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.IgnoreAcceptorHostname = v
|
||||
case "k5login_authoritative":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.K5LoginAuthoritative = v
|
||||
case "k5login_directory":
|
||||
l.K5LoginDirectory = strings.TrimSpace(p[1])
|
||||
case "kdc_default_options":
|
||||
v := strings.TrimSpace(p[1])
|
||||
v = strings.Replace(v, "0x", "", -1)
|
||||
b, err := hex.DecodeString(v)
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.KDCDefaultOptions.Bytes = b
|
||||
l.KDCDefaultOptions.BitLength = len(b) * 8
|
||||
case "kdc_timesync":
|
||||
p[1] = strings.TrimSpace(p[1])
|
||||
v, err := strconv.ParseInt(p[1], 10, 32)
|
||||
if err != nil || v < 0 {
|
||||
return InvalidErrorf("libdefaults section line (%s)", line)
|
||||
}
|
||||
l.KDCTimeSync = int(v)
|
||||
case "noaddresses":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.NoAddresses = v
|
||||
case "permitted_enctypes":
|
||||
l.PermittedEnctypes = strings.Fields(p[1])
|
||||
case "preferred_preauth_types":
|
||||
p[1] = strings.TrimSpace(p[1])
|
||||
t := strings.Split(p[1], ",")
|
||||
var v []int
|
||||
for _, s := range t {
|
||||
i, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
v = append(v, int(i))
|
||||
}
|
||||
l.PreferredPreauthTypes = v
|
||||
case "proxiable":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.Proxiable = v
|
||||
case "rdns":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.RDNS = v
|
||||
case "realm_try_domains":
|
||||
p[1] = strings.TrimSpace(p[1])
|
||||
v, err := strconv.ParseInt(p[1], 10, 32)
|
||||
if err != nil || v < -1 {
|
||||
return InvalidErrorf("libdefaults section line (%s)", line)
|
||||
}
|
||||
l.RealmTryDomains = int(v)
|
||||
case "renew_lifetime":
|
||||
d, err := parseDuration(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.RenewLifetime = d
|
||||
case "safe_checksum_type":
|
||||
p[1] = strings.TrimSpace(p[1])
|
||||
v, err := strconv.ParseInt(p[1], 10, 32)
|
||||
if err != nil || v < 0 {
|
||||
return InvalidErrorf("libdefaults section line (%s)", line)
|
||||
}
|
||||
l.SafeChecksumType = int(v)
|
||||
case "ticket_lifetime":
|
||||
d, err := parseDuration(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.TicketLifetime = d
|
||||
case "udp_preference_limit":
|
||||
p[1] = strings.TrimSpace(p[1])
|
||||
v, err := strconv.ParseUint(p[1], 10, 32)
|
||||
if err != nil || v > 32700 {
|
||||
return InvalidErrorf("libdefaults section line (%s)", line)
|
||||
}
|
||||
l.UDPPreferenceLimit = int(v)
|
||||
case "verify_ap_req_nofail":
|
||||
v, err := parseBoolean(p[1])
|
||||
if err != nil {
|
||||
return InvalidErrorf("libdefaults section line (%s): %v", line, err)
|
||||
}
|
||||
l.VerifyAPReqNofail = v
|
||||
default:
|
||||
//Ignore the line
|
||||
continue
|
||||
}
|
||||
}
|
||||
l.DefaultTGSEnctypeIDs = parseETypes(l.DefaultTGSEnctypes, l.AllowWeakCrypto)
|
||||
l.DefaultTktEnctypeIDs = parseETypes(l.DefaultTktEnctypes, l.AllowWeakCrypto)
|
||||
l.PermittedEnctypeIDs = parseETypes(l.PermittedEnctypes, l.AllowWeakCrypto)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Realm represents an entry in the [realms] section of the configuration.
|
||||
type Realm struct {
|
||||
Realm string
|
||||
AdminServer []string
|
||||
//auth_to_local //Not implementing for now
|
||||
//auth_to_local_names //Not implementing for now
|
||||
DefaultDomain string
|
||||
KDC []string
|
||||
KPasswdServer []string //default admin_server:464
|
||||
MasterKDC []string
|
||||
}
|
||||
|
||||
// Parse the lines of a [realms] entry into the Realm struct.
|
||||
func (r *Realm) parseLines(name string, lines []string) (err error) {
|
||||
r.Realm = name
|
||||
var adminServerFinal bool
|
||||
var KDCFinal bool
|
||||
var kpasswdServerFinal bool
|
||||
var masterKDCFinal bool
|
||||
var ignore bool
|
||||
var c int // counts the depth of blocks within brackets { }
|
||||
for _, line := range lines {
|
||||
if ignore && c > 0 && !strings.Contains(line, "{") && !strings.Contains(line, "}") {
|
||||
continue
|
||||
}
|
||||
//Remove comments after the values
|
||||
if idx := strings.IndexAny(line, "#;"); idx != -1 {
|
||||
line = line[:idx]
|
||||
}
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(line, "=") && !strings.Contains(line, "}") {
|
||||
return InvalidErrorf("realms section line (%s)", line)
|
||||
}
|
||||
if strings.Contains(line, "v4_") {
|
||||
ignore = true
|
||||
err = UnsupportedDirective{"v4 configurations are not supported"}
|
||||
}
|
||||
if strings.Contains(line, "{") {
|
||||
c++
|
||||
if ignore {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if strings.Contains(line, "}") {
|
||||
c--
|
||||
if c < 0 {
|
||||
return InvalidErrorf("unpaired curly brackets")
|
||||
}
|
||||
if ignore {
|
||||
if c < 1 {
|
||||
c = 0
|
||||
ignore = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
p := strings.Split(line, "=")
|
||||
key := strings.TrimSpace(strings.ToLower(p[0]))
|
||||
v := strings.TrimSpace(p[1])
|
||||
switch key {
|
||||
case "admin_server":
|
||||
appendUntilFinal(&r.AdminServer, v, &adminServerFinal)
|
||||
case "default_domain":
|
||||
r.DefaultDomain = v
|
||||
case "kdc":
|
||||
if !strings.Contains(v, ":") {
|
||||
// No port number specified default to 88
|
||||
if strings.HasSuffix(v, `*`) {
|
||||
v = strings.TrimSpace(strings.TrimSuffix(v, `*`)) + ":88*"
|
||||
} else {
|
||||
v = strings.TrimSpace(v) + ":88"
|
||||
}
|
||||
}
|
||||
appendUntilFinal(&r.KDC, v, &KDCFinal)
|
||||
case "kpasswd_server":
|
||||
appendUntilFinal(&r.KPasswdServer, v, &kpasswdServerFinal)
|
||||
case "master_kdc":
|
||||
appendUntilFinal(&r.MasterKDC, v, &masterKDCFinal)
|
||||
default:
|
||||
//Ignore the line
|
||||
continue
|
||||
}
|
||||
}
|
||||
//default for Kpasswd_server = admin_server:464
|
||||
if len(r.KPasswdServer) < 1 {
|
||||
for _, a := range r.AdminServer {
|
||||
s := strings.Split(a, ":")
|
||||
r.KPasswdServer = append(r.KPasswdServer, s[0]+":464")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the lines of the [realms] section of the configuration into an slice of Realm structs.
|
||||
func parseRealms(lines []string) (realms []Realm, err error) {
|
||||
var name string
|
||||
var start int
|
||||
var c int
|
||||
for i, l := range lines {
|
||||
//Remove comments after the values
|
||||
if idx := strings.IndexAny(l, "#;"); idx != -1 {
|
||||
l = l[:idx]
|
||||
}
|
||||
l = strings.TrimSpace(l)
|
||||
if l == "" {
|
||||
continue
|
||||
}
|
||||
//if strings.Contains(l, "v4_") {
|
||||
// return nil, errors.New("v4 configurations are not supported in Realms section")
|
||||
//}
|
||||
if strings.Contains(l, "{") {
|
||||
c++
|
||||
if !strings.Contains(l, "=") {
|
||||
return nil, fmt.Errorf("realm configuration line invalid: %s", l)
|
||||
}
|
||||
if c == 1 {
|
||||
start = i
|
||||
p := strings.Split(l, "=")
|
||||
name = strings.TrimSpace(p[0])
|
||||
}
|
||||
}
|
||||
if strings.Contains(l, "}") {
|
||||
if c < 1 {
|
||||
// but not started a block!!!
|
||||
return nil, errors.New("invalid Realms section in configuration")
|
||||
}
|
||||
c--
|
||||
if c == 0 {
|
||||
var r Realm
|
||||
e := r.parseLines(name, lines[start+1:i])
|
||||
if e != nil {
|
||||
if _, ok := e.(UnsupportedDirective); !ok {
|
||||
err = e
|
||||
return
|
||||
}
|
||||
err = e
|
||||
}
|
||||
realms = append(realms, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DomainRealm maps the domains to realms representing the [domain_realm] section of the configuration.
|
||||
type DomainRealm map[string]string
|
||||
|
||||
// Parse the lines of the [domain_realm] section of the configuration and add to the mapping.
|
||||
func (d *DomainRealm) parseLines(lines []string) error {
|
||||
for _, line := range lines {
|
||||
//Remove comments after the values
|
||||
if idx := strings.IndexAny(line, "#;"); idx != -1 {
|
||||
line = line[:idx]
|
||||
}
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(line, "=") {
|
||||
return InvalidErrorf("realm line (%s)", line)
|
||||
}
|
||||
p := strings.Split(line, "=")
|
||||
domain := strings.TrimSpace(strings.ToLower(p[0]))
|
||||
realm := strings.TrimSpace(p[1])
|
||||
d.addMapping(domain, realm)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add a domain to realm mapping.
|
||||
func (d *DomainRealm) addMapping(domain, realm string) {
|
||||
(*d)[domain] = realm
|
||||
}
|
||||
|
||||
// Delete a domain to realm mapping.
|
||||
func (d *DomainRealm) deleteMapping(domain, realm string) {
|
||||
delete(*d, domain)
|
||||
}
|
||||
|
||||
// ResolveRealm resolves the kerberos realm for the specified domain name from the domain to realm mapping.
|
||||
// The most specific mapping is returned.
|
||||
func (c *Config) ResolveRealm(domainName string) string {
|
||||
domainName = strings.TrimSuffix(domainName, ".")
|
||||
|
||||
// Try to match the entire hostname first
|
||||
if r, ok := c.DomainRealm[domainName]; ok {
|
||||
return r
|
||||
}
|
||||
|
||||
// Try to match all DNS domain parts
|
||||
periods := strings.Count(domainName, ".") + 1
|
||||
for i := 2; i <= periods; i++ {
|
||||
z := strings.SplitN(domainName, ".", i)
|
||||
if r, ok := c.DomainRealm["."+z[len(z)-1]]; ok {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return c.LibDefaults.DefaultRealm
|
||||
}
|
||||
|
||||
// Load the KRB5 configuration from the specified file path.
|
||||
func Load(cfgPath string) (*Config, error) {
|
||||
fh, err := os.Open(cfgPath)
|
||||
if err != nil {
|
||||
return nil, errors.New("configuration file could not be opened: " + cfgPath + " " + err.Error())
|
||||
}
|
||||
defer fh.Close()
|
||||
scanner := bufio.NewScanner(fh)
|
||||
return NewConfigFromScanner(scanner)
|
||||
}
|
||||
|
||||
// NewConfigFromString creates a new Config struct from a string.
|
||||
func NewConfigFromString(s string) (*Config, error) {
|
||||
reader := strings.NewReader(s)
|
||||
return NewConfigFromReader(reader)
|
||||
}
|
||||
|
||||
// NewConfigFromReader creates a new Config struct from an io.Reader.
|
||||
func NewConfigFromReader(r io.Reader) (*Config, error) {
|
||||
scanner := bufio.NewScanner(r)
|
||||
return NewConfigFromScanner(scanner)
|
||||
}
|
||||
|
||||
// NewConfigFromScanner creates a new Config struct from a bufio.Scanner.
|
||||
func NewConfigFromScanner(scanner *bufio.Scanner) (*Config, error) {
|
||||
c := NewConfig()
|
||||
var e error
|
||||
sections := make(map[int]string)
|
||||
var sectionLineNum []int
|
||||
var lines []string
|
||||
for scanner.Scan() {
|
||||
// Skip comments and blank lines
|
||||
if matched, _ := regexp.MatchString(`^\s*(#|;|\n)`, scanner.Text()); matched {
|
||||
continue
|
||||
}
|
||||
if matched, _ := regexp.MatchString(`^\s*\[libdefaults\]\s*`, scanner.Text()); matched {
|
||||
sections[len(lines)] = "libdefaults"
|
||||
sectionLineNum = append(sectionLineNum, len(lines))
|
||||
continue
|
||||
}
|
||||
if matched, _ := regexp.MatchString(`^\s*\[realms\]\s*`, scanner.Text()); matched {
|
||||
sections[len(lines)] = "realms"
|
||||
sectionLineNum = append(sectionLineNum, len(lines))
|
||||
continue
|
||||
}
|
||||
if matched, _ := regexp.MatchString(`^\s*\[domain_realm\]\s*`, scanner.Text()); matched {
|
||||
sections[len(lines)] = "domain_realm"
|
||||
sectionLineNum = append(sectionLineNum, len(lines))
|
||||
continue
|
||||
}
|
||||
if matched, _ := regexp.MatchString(`^\s*\[.*\]\s*`, scanner.Text()); matched {
|
||||
sections[len(lines)] = "unknown_section"
|
||||
sectionLineNum = append(sectionLineNum, len(lines))
|
||||
continue
|
||||
}
|
||||
lines = append(lines, scanner.Text())
|
||||
}
|
||||
for i, start := range sectionLineNum {
|
||||
var end int
|
||||
if i+1 >= len(sectionLineNum) {
|
||||
end = len(lines)
|
||||
} else {
|
||||
end = sectionLineNum[i+1]
|
||||
}
|
||||
switch section := sections[start]; section {
|
||||
case "libdefaults":
|
||||
err := c.LibDefaults.parseLines(lines[start:end])
|
||||
if err != nil {
|
||||
if _, ok := err.(UnsupportedDirective); !ok {
|
||||
return nil, fmt.Errorf("error processing libdefaults section: %v", err)
|
||||
}
|
||||
e = err
|
||||
}
|
||||
case "realms":
|
||||
realms, err := parseRealms(lines[start:end])
|
||||
if err != nil {
|
||||
if _, ok := err.(UnsupportedDirective); !ok {
|
||||
return nil, fmt.Errorf("error processing realms section: %v", err)
|
||||
}
|
||||
e = err
|
||||
}
|
||||
c.Realms = realms
|
||||
case "domain_realm":
|
||||
err := c.DomainRealm.parseLines(lines[start:end])
|
||||
if err != nil {
|
||||
if _, ok := err.(UnsupportedDirective); !ok {
|
||||
return nil, fmt.Errorf("error processing domaain_realm section: %v", err)
|
||||
}
|
||||
e = err
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
return c, e
|
||||
}
|
||||
|
||||
// Parse a space delimited list of ETypes into a list of EType numbers optionally filtering out weak ETypes.
|
||||
func parseETypes(s []string, w bool) []int32 {
|
||||
var eti []int32
|
||||
for _, et := range s {
|
||||
if !w {
|
||||
var weak bool
|
||||
for _, wet := range strings.Fields(WeakETypeList) {
|
||||
if et == wet {
|
||||
weak = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if weak {
|
||||
continue
|
||||
}
|
||||
}
|
||||
i := etypeID.EtypeSupported(et)
|
||||
if i != 0 {
|
||||
eti = append(eti, i)
|
||||
}
|
||||
}
|
||||
return eti
|
||||
}
|
||||
|
||||
// Parse a time duration string in the configuration to a golang time.Duration.
|
||||
func parseDuration(s string) (time.Duration, error) {
|
||||
s = strings.Replace(strings.TrimSpace(s), " ", "", -1)
|
||||
|
||||
// handle Nd[NmNs]
|
||||
if strings.Contains(s, "d") {
|
||||
ds := strings.SplitN(s, "d", 2)
|
||||
dn, err := strconv.ParseUint(ds[0], 10, 32)
|
||||
if err != nil {
|
||||
return time.Duration(0), errors.New("invalid time duration")
|
||||
}
|
||||
d := time.Duration(dn*24) * time.Hour
|
||||
if ds[1] != "" {
|
||||
dp, err := time.ParseDuration(ds[1])
|
||||
if err != nil {
|
||||
return time.Duration(0), errors.New("invalid time duration")
|
||||
}
|
||||
d = d + dp
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// handle Nm[Ns]
|
||||
d, err := time.ParseDuration(s)
|
||||
if err == nil {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// handle N
|
||||
v, err := strconv.ParseUint(s, 10, 32)
|
||||
if err == nil && v > 0 {
|
||||
return time.Duration(v) * time.Second, nil
|
||||
}
|
||||
|
||||
// handle h:m[:s]
|
||||
if strings.Contains(s, ":") {
|
||||
t := strings.Split(s, ":")
|
||||
if 2 > len(t) || len(t) > 3 {
|
||||
return time.Duration(0), errors.New("invalid time duration value")
|
||||
}
|
||||
var i []int
|
||||
for _, n := range t {
|
||||
j, err := strconv.ParseInt(n, 10, 16)
|
||||
if err != nil {
|
||||
return time.Duration(0), errors.New("invalid time duration value")
|
||||
}
|
||||
i = append(i, int(j))
|
||||
}
|
||||
d := time.Duration(i[0])*time.Hour + time.Duration(i[1])*time.Minute
|
||||
if len(i) == 3 {
|
||||
d = d + time.Duration(i[2])*time.Second
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
return time.Duration(0), errors.New("invalid time duration value")
|
||||
}
|
||||
|
||||
// Parse possible boolean values to golang bool.
|
||||
func parseBoolean(s string) (bool, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
v, err := strconv.ParseBool(s)
|
||||
if err == nil {
|
||||
return v, nil
|
||||
}
|
||||
switch strings.ToLower(s) {
|
||||
case "yes":
|
||||
return true, nil
|
||||
case "y":
|
||||
return true, nil
|
||||
case "no":
|
||||
return false, nil
|
||||
case "n":
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.New("invalid boolean value")
|
||||
}
|
||||
|
||||
// Parse array of strings but stop if an asterisk is placed at the end of a line.
|
||||
func appendUntilFinal(s *[]string, value string, final *bool) {
|
||||
if *final {
|
||||
return
|
||||
}
|
||||
if last := len(value) - 1; last >= 0 && value[last] == '*' {
|
||||
*final = true
|
||||
value = value[:len(value)-1]
|
||||
}
|
||||
*s = append(*s, value)
|
||||
}
|
||||
+348
@@ -0,0 +1,348 @@
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/jcmturner/gofork/encoding/asn1"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/types"
|
||||
)
|
||||
|
||||
const (
|
||||
headerFieldTagKDCOffset = 1
|
||||
)
|
||||
|
||||
// The first byte of the file always has the value 5.
|
||||
// The value of the second byte contains the version number (1 through 4)
|
||||
// Versions 1 and 2 of the file format use native byte order for integer representations.
|
||||
// Versions 3 and 4 always use big-endian byte order
|
||||
// After the two-byte version indicator, the file has three parts:
|
||||
// 1) the header (in version 4 only)
|
||||
// 2) the default principal name
|
||||
// 3) a sequence of credentials
|
||||
|
||||
// CCache is the file credentials cache as define here: https://web.mit.edu/kerberos/krb5-latest/doc/formats/ccache_file_format.html
|
||||
type CCache struct {
|
||||
Version uint8
|
||||
Header header
|
||||
DefaultPrincipal principal
|
||||
Credentials []*Credential
|
||||
Path string
|
||||
}
|
||||
|
||||
type header struct {
|
||||
length uint16
|
||||
fields []headerField
|
||||
}
|
||||
|
||||
type headerField struct {
|
||||
tag uint16
|
||||
length uint16
|
||||
value []byte
|
||||
}
|
||||
|
||||
// Credential cache entry principal struct.
|
||||
type principal struct {
|
||||
Realm string
|
||||
PrincipalName types.PrincipalName
|
||||
}
|
||||
|
||||
// Credential holds a Kerberos client's ccache credential information.
|
||||
type Credential struct {
|
||||
Client principal
|
||||
Server principal
|
||||
Key types.EncryptionKey
|
||||
AuthTime time.Time
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
RenewTill time.Time
|
||||
IsSKey bool
|
||||
TicketFlags asn1.BitString
|
||||
Addresses []types.HostAddress
|
||||
AuthData []types.AuthorizationDataEntry
|
||||
Ticket []byte
|
||||
SecondTicket []byte
|
||||
}
|
||||
|
||||
// LoadCCache loads a credential cache file into a CCache type.
|
||||
func LoadCCache(cpath string) (*CCache, error) {
|
||||
c := new(CCache)
|
||||
b, err := ioutil.ReadFile(cpath)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
err = c.Unmarshal(b)
|
||||
return c, err
|
||||
}
|
||||
|
||||
// Unmarshal a byte slice of credential cache data into CCache type.
|
||||
func (c *CCache) Unmarshal(b []byte) error {
|
||||
p := 0
|
||||
//The first byte of the file always has the value 5
|
||||
if int8(b[p]) != 5 {
|
||||
return errors.New("Invalid credential cache data. First byte does not equal 5")
|
||||
}
|
||||
p++
|
||||
//Get credential cache version
|
||||
//The second byte contains the version number (1 to 4)
|
||||
c.Version = b[p]
|
||||
if c.Version < 1 || c.Version > 4 {
|
||||
return errors.New("Invalid credential cache data. Keytab version is not within 1 to 4")
|
||||
}
|
||||
p++
|
||||
//Version 1 or 2 of the file format uses native byte order for integer representations. Versions 3 & 4 always uses big-endian byte order
|
||||
var endian binary.ByteOrder
|
||||
endian = binary.BigEndian
|
||||
if (c.Version == 1 || c.Version == 2) && isNativeEndianLittle() {
|
||||
endian = binary.LittleEndian
|
||||
}
|
||||
if c.Version == 4 {
|
||||
err := parseHeader(b, &p, c, &endian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.DefaultPrincipal = parsePrincipal(b, &p, c, &endian)
|
||||
for p < len(b) {
|
||||
cred, err := parseCredential(b, &p, c, &endian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Credentials = append(c.Credentials, cred)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseHeader(b []byte, p *int, c *CCache, e *binary.ByteOrder) error {
|
||||
if c.Version != 4 {
|
||||
return errors.New("Credentials cache version is not 4 so there is no header to parse.")
|
||||
}
|
||||
h := header{}
|
||||
h.length = uint16(readInt16(b, p, e))
|
||||
for *p <= int(h.length) {
|
||||
f := headerField{}
|
||||
f.tag = uint16(readInt16(b, p, e))
|
||||
f.length = uint16(readInt16(b, p, e))
|
||||
f.value = b[*p : *p+int(f.length)]
|
||||
*p += int(f.length)
|
||||
if !f.valid() {
|
||||
return errors.New("Invalid credential cache header found")
|
||||
}
|
||||
h.fields = append(h.fields, f)
|
||||
}
|
||||
c.Header = h
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse the Keytab bytes of a principal into a Keytab entry's principal.
|
||||
func parsePrincipal(b []byte, p *int, c *CCache, e *binary.ByteOrder) (princ principal) {
|
||||
if c.Version != 1 {
|
||||
//Name Type is omitted in version 1
|
||||
princ.PrincipalName.NameType = readInt32(b, p, e)
|
||||
}
|
||||
nc := int(readInt32(b, p, e))
|
||||
if c.Version == 1 {
|
||||
//In version 1 the number of components includes the realm. Minus 1 to make consistent with version 2
|
||||
nc--
|
||||
}
|
||||
lenRealm := readInt32(b, p, e)
|
||||
princ.Realm = string(readBytes(b, p, int(lenRealm), e))
|
||||
for i := 0; i < nc; i++ {
|
||||
l := readInt32(b, p, e)
|
||||
princ.PrincipalName.NameString = append(princ.PrincipalName.NameString, string(readBytes(b, p, int(l), e)))
|
||||
}
|
||||
return princ
|
||||
}
|
||||
|
||||
func parseCredential(b []byte, p *int, c *CCache, e *binary.ByteOrder) (cred *Credential, err error) {
|
||||
cred = new(Credential)
|
||||
cred.Client = parsePrincipal(b, p, c, e)
|
||||
cred.Server = parsePrincipal(b, p, c, e)
|
||||
key := types.EncryptionKey{}
|
||||
key.KeyType = int32(readInt16(b, p, e))
|
||||
if c.Version == 3 {
|
||||
//repeated twice in version 3
|
||||
key.KeyType = int32(readInt16(b, p, e))
|
||||
}
|
||||
key.KeyValue = readData(b, p, e)
|
||||
cred.Key = key
|
||||
cred.AuthTime = readTimestamp(b, p, e)
|
||||
cred.StartTime = readTimestamp(b, p, e)
|
||||
cred.EndTime = readTimestamp(b, p, e)
|
||||
cred.RenewTill = readTimestamp(b, p, e)
|
||||
if ik := readInt8(b, p, e); ik == 0 {
|
||||
cred.IsSKey = false
|
||||
} else {
|
||||
cred.IsSKey = true
|
||||
}
|
||||
cred.TicketFlags = types.NewKrbFlags()
|
||||
cred.TicketFlags.Bytes = readBytes(b, p, 4, e)
|
||||
l := int(readInt32(b, p, e))
|
||||
cred.Addresses = make([]types.HostAddress, l, l)
|
||||
for i := range cred.Addresses {
|
||||
cred.Addresses[i] = readAddress(b, p, e)
|
||||
}
|
||||
l = int(readInt32(b, p, e))
|
||||
cred.AuthData = make([]types.AuthorizationDataEntry, l, l)
|
||||
for i := range cred.AuthData {
|
||||
cred.AuthData[i] = readAuthDataEntry(b, p, e)
|
||||
}
|
||||
cred.Ticket = readData(b, p, e)
|
||||
cred.SecondTicket = readData(b, p, e)
|
||||
return
|
||||
}
|
||||
|
||||
// GetClientPrincipalName returns a PrincipalName type for the client the credentials cache is for.
|
||||
func (c *CCache) GetClientPrincipalName() types.PrincipalName {
|
||||
return c.DefaultPrincipal.PrincipalName
|
||||
}
|
||||
|
||||
// GetClientRealm returns the reals of the client the credentials cache is for.
|
||||
func (c *CCache) GetClientRealm() string {
|
||||
return c.DefaultPrincipal.Realm
|
||||
}
|
||||
|
||||
// GetClientCredentials returns a Credentials object representing the client of the credentials cache.
|
||||
func (c *CCache) GetClientCredentials() *Credentials {
|
||||
return &Credentials{
|
||||
username: c.DefaultPrincipal.PrincipalName.PrincipalNameString(),
|
||||
realm: c.GetClientRealm(),
|
||||
cname: c.DefaultPrincipal.PrincipalName,
|
||||
}
|
||||
}
|
||||
|
||||
// Contains tests if the cache contains a credential for the provided server PrincipalName
|
||||
func (c *CCache) Contains(p types.PrincipalName) bool {
|
||||
for _, cred := range c.Credentials {
|
||||
if cred.Server.PrincipalName.Equal(p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetEntry returns a specific credential for the PrincipalName provided.
|
||||
func (c *CCache) GetEntry(p types.PrincipalName) (*Credential, bool) {
|
||||
cred := new(Credential)
|
||||
var found bool
|
||||
for i := range c.Credentials {
|
||||
if c.Credentials[i].Server.PrincipalName.Equal(p) {
|
||||
cred = c.Credentials[i]
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return cred, false
|
||||
}
|
||||
return cred, true
|
||||
}
|
||||
|
||||
// GetEntries filters out configuration entries an returns a slice of credentials.
|
||||
func (c *CCache) GetEntries() []*Credential {
|
||||
creds := make([]*Credential, 0)
|
||||
for _, cred := range c.Credentials {
|
||||
// Filter out configuration entries
|
||||
if strings.HasPrefix(cred.Server.Realm, "X-CACHECONF") {
|
||||
continue
|
||||
}
|
||||
creds = append(creds, cred)
|
||||
}
|
||||
return creds
|
||||
}
|
||||
|
||||
func (h *headerField) valid() bool {
|
||||
// At this time there is only one defined header field.
|
||||
// Its tag value is 1, its length is always 8.
|
||||
// Its contents are two 32-bit integers giving the seconds and microseconds
|
||||
// of the time offset of the KDC relative to the client.
|
||||
// Adding this offset to the current time on the client should give the current time on the KDC, if that offset has not changed since the initial authentication.
|
||||
|
||||
// Done as a switch in case other tag values are added in the future.
|
||||
switch h.tag {
|
||||
case headerFieldTagKDCOffset:
|
||||
if h.length != 8 || len(h.value) != 8 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func readData(b []byte, p *int, e *binary.ByteOrder) []byte {
|
||||
l := readInt32(b, p, e)
|
||||
return readBytes(b, p, int(l), e)
|
||||
}
|
||||
|
||||
func readAddress(b []byte, p *int, e *binary.ByteOrder) types.HostAddress {
|
||||
a := types.HostAddress{}
|
||||
a.AddrType = int32(readInt16(b, p, e))
|
||||
a.Address = readData(b, p, e)
|
||||
return a
|
||||
}
|
||||
|
||||
func readAuthDataEntry(b []byte, p *int, e *binary.ByteOrder) types.AuthorizationDataEntry {
|
||||
a := types.AuthorizationDataEntry{}
|
||||
a.ADType = int32(readInt16(b, p, e))
|
||||
a.ADData = readData(b, p, e)
|
||||
return a
|
||||
}
|
||||
|
||||
// Read bytes representing a timestamp.
|
||||
func readTimestamp(b []byte, p *int, e *binary.ByteOrder) time.Time {
|
||||
return time.Unix(int64(readInt32(b, p, e)), 0)
|
||||
}
|
||||
|
||||
// Read bytes representing an eight bit integer.
|
||||
func readInt8(b []byte, p *int, e *binary.ByteOrder) (i int8) {
|
||||
buf := bytes.NewBuffer(b[*p : *p+1])
|
||||
binary.Read(buf, *e, &i)
|
||||
*p++
|
||||
return
|
||||
}
|
||||
|
||||
// Read bytes representing a sixteen bit integer.
|
||||
func readInt16(b []byte, p *int, e *binary.ByteOrder) (i int16) {
|
||||
buf := bytes.NewBuffer(b[*p : *p+2])
|
||||
binary.Read(buf, *e, &i)
|
||||
*p += 2
|
||||
return
|
||||
}
|
||||
|
||||
// Read bytes representing a thirty two bit integer.
|
||||
func readInt32(b []byte, p *int, e *binary.ByteOrder) (i int32) {
|
||||
buf := bytes.NewBuffer(b[*p : *p+4])
|
||||
binary.Read(buf, *e, &i)
|
||||
*p += 4
|
||||
return
|
||||
}
|
||||
|
||||
func readBytes(b []byte, p *int, s int, e *binary.ByteOrder) []byte {
|
||||
buf := bytes.NewBuffer(b[*p : *p+s])
|
||||
r := make([]byte, s)
|
||||
binary.Read(buf, *e, &r)
|
||||
*p += s
|
||||
return r
|
||||
}
|
||||
|
||||
func isNativeEndianLittle() bool {
|
||||
var x = 0x012345678
|
||||
var p = unsafe.Pointer(&x)
|
||||
var bp = (*[4]byte)(p)
|
||||
|
||||
var endian bool
|
||||
if 0x01 == bp[0] {
|
||||
endian = false
|
||||
} else if (0x78 & 0xff) == (bp[0] & 0xff) {
|
||||
endian = true
|
||||
} else {
|
||||
// Default to big endian
|
||||
endian = false
|
||||
}
|
||||
return endian
|
||||
}
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
// Package credentials provides credentials management for Kerberos 5 authentication.
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/iana/nametype"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/keytab"
|
||||
"gopkg.in/jcmturner/gokrb5.v7/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// AttributeKeyADCredentials assigned number for AD credentials.
|
||||
AttributeKeyADCredentials = "gokrb5AttributeKeyADCredentials"
|
||||
)
|
||||
|
||||
// Credentials struct for a user.
|
||||
// Contains either a keytab, password or both.
|
||||
// Keytabs are used over passwords if both are defined.
|
||||
type Credentials struct {
|
||||
username string
|
||||
displayName string
|
||||
realm string
|
||||
cname types.PrincipalName
|
||||
keytab *keytab.Keytab
|
||||
password string
|
||||
attributes map[string]interface{}
|
||||
validUntil time.Time
|
||||
|
||||
authenticated bool
|
||||
human bool
|
||||
authTime time.Time
|
||||
groupMembership map[string]bool
|
||||
sessionID string
|
||||
}
|
||||
|
||||
// ADCredentials contains information obtained from the PAC.
|
||||
type ADCredentials struct {
|
||||
EffectiveName string
|
||||
FullName string
|
||||
UserID int
|
||||
PrimaryGroupID int
|
||||
LogOnTime time.Time
|
||||
LogOffTime time.Time
|
||||
PasswordLastSet time.Time
|
||||
GroupMembershipSIDs []string
|
||||
LogonDomainName string
|
||||
LogonDomainID string
|
||||
LogonServer string
|
||||
}
|
||||
|
||||
// New creates a new Credentials instance.
|
||||
func New(username string, realm string) *Credentials {
|
||||
uid, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
uid = "00unique-sess-ions-uuid-unavailable0"
|
||||
}
|
||||
return &Credentials{
|
||||
username: username,
|
||||
displayName: username,
|
||||
realm: realm,
|
||||
cname: types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, username),
|
||||
keytab: keytab.New(),
|
||||
attributes: make(map[string]interface{}),
|
||||
groupMembership: make(map[string]bool),
|
||||
sessionID: uid,
|
||||
human: true,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFromPrincipalName creates a new Credentials instance with the user details provides as a PrincipalName type.
|
||||
func NewFromPrincipalName(cname types.PrincipalName, realm string) *Credentials {
|
||||
uid, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
uid = "00unique-sess-ions-uuid-unavailable0"
|
||||
}
|
||||
return &Credentials{
|
||||
username: cname.PrincipalNameString(),
|
||||
displayName: cname.PrincipalNameString(),
|
||||
realm: realm,
|
||||
cname: cname,
|
||||
keytab: keytab.New(),
|
||||
attributes: make(map[string]interface{}),
|
||||
groupMembership: make(map[string]bool),
|
||||
sessionID: uid,
|
||||
human: true,
|
||||
}
|
||||
}
|
||||
|
||||
// WithKeytab sets the Keytab in the Credentials struct.
|
||||
func (c *Credentials) WithKeytab(kt *keytab.Keytab) *Credentials {
|
||||
c.keytab = kt
|
||||
c.password = ""
|
||||
return c
|
||||
}
|
||||
|
||||
// Keytab returns the credential's Keytab.
|
||||
func (c *Credentials) Keytab() *keytab.Keytab {
|
||||
return c.keytab
|
||||
}
|
||||
|
||||
// HasKeytab queries if the Credentials has a keytab defined.
|
||||
func (c *Credentials) HasKeytab() bool {
|
||||
if c.keytab != nil && len(c.keytab.Entries) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WithPassword sets the password in the Credentials struct.
|
||||
func (c *Credentials) WithPassword(password string) *Credentials {
|
||||
c.password = password
|
||||
c.keytab = keytab.New() // clear any keytab
|
||||
return c
|
||||
}
|
||||
|
||||
// Password returns the credential's password.
|
||||
func (c *Credentials) Password() string {
|
||||
return c.password
|
||||
}
|
||||
|
||||
// HasPassword queries if the Credentials has a password defined.
|
||||
func (c *Credentials) HasPassword() bool {
|
||||
if c.password != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetValidUntil sets the expiry time of the credentials
|
||||
func (c *Credentials) SetValidUntil(t time.Time) {
|
||||
c.validUntil = t
|
||||
}
|
||||
|
||||
// SetADCredentials adds ADCredentials attributes to the credentials
|
||||
func (c *Credentials) SetADCredentials(a ADCredentials) {
|
||||
c.SetAttribute(AttributeKeyADCredentials, a)
|
||||
if a.FullName != "" {
|
||||
c.SetDisplayName(a.FullName)
|
||||
}
|
||||
if a.EffectiveName != "" {
|
||||
c.SetUserName(a.EffectiveName)
|
||||
}
|
||||
for i := range a.GroupMembershipSIDs {
|
||||
c.AddAuthzAttribute(a.GroupMembershipSIDs[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Methods to implement goidentity.Identity interface
|
||||
|
||||
// UserName returns the credential's username.
|
||||
func (c *Credentials) UserName() string {
|
||||
return c.username
|
||||
}
|
||||
|
||||
// SetUserName sets the username value on the credential.
|
||||
func (c *Credentials) SetUserName(s string) {
|
||||
c.username = s
|
||||
}
|
||||
|
||||
// CName returns the credential's client principal name.
|
||||
func (c *Credentials) CName() types.PrincipalName {
|
||||
return c.cname
|
||||
}
|
||||
|
||||
// SetCName sets the client principal name on the credential.
|
||||
func (c *Credentials) SetCName(pn types.PrincipalName) {
|
||||
c.cname = pn
|
||||
}
|
||||
|
||||
// Domain returns the credential's domain.
|
||||
func (c *Credentials) Domain() string {
|
||||
return c.realm
|
||||
}
|
||||
|
||||
// SetDomain sets the domain value on the credential.
|
||||
func (c *Credentials) SetDomain(s string) {
|
||||
c.realm = s
|
||||
}
|
||||
|
||||
// Realm returns the credential's realm. Same as the domain.
|
||||
func (c *Credentials) Realm() string {
|
||||
return c.Domain()
|
||||
}
|
||||
|
||||
// SetRealm sets the realm value on the credential. Same as the domain
|
||||
func (c *Credentials) SetRealm(s string) {
|
||||
c.SetDomain(s)
|
||||
}
|
||||
|
||||
// DisplayName returns the credential's display name.
|
||||
func (c *Credentials) DisplayName() string {
|
||||
return c.displayName
|
||||
}
|
||||
|
||||
// SetDisplayName sets the display name value on the credential.
|
||||
func (c *Credentials) SetDisplayName(s string) {
|
||||
c.displayName = s
|
||||
}
|
||||
|
||||
// Human returns if the credential represents a human or not.
|
||||
func (c *Credentials) Human() bool {
|
||||
return c.human
|
||||
}
|
||||
|
||||
// SetHuman sets the credential as human.
|
||||
func (c *Credentials) SetHuman(b bool) {
|
||||
c.human = b
|
||||
}
|
||||
|
||||
// AuthTime returns the time the credential was authenticated.
|
||||
func (c *Credentials) AuthTime() time.Time {
|
||||
return c.authTime
|
||||
}
|
||||
|
||||
// SetAuthTime sets the time the credential was authenticated.
|
||||
func (c *Credentials) SetAuthTime(t time.Time) {
|
||||
c.authTime = t
|
||||
}
|
||||
|
||||
// AuthzAttributes returns the credentials authorizing attributes.
|
||||
func (c *Credentials) AuthzAttributes() []string {
|
||||
s := make([]string, len(c.groupMembership))
|
||||
i := 0
|
||||
for a := range c.groupMembership {
|
||||
s[i] = a
|
||||
i++
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Authenticated indicates if the credential has been successfully authenticated or not.
|
||||
func (c *Credentials) Authenticated() bool {
|
||||
return c.authenticated
|
||||
}
|
||||
|
||||
// SetAuthenticated sets the credential as having been successfully authenticated.
|
||||
func (c *Credentials) SetAuthenticated(b bool) {
|
||||
c.authenticated = b
|
||||
}
|
||||
|
||||
// AddAuthzAttribute adds an authorization attribute to the credential.
|
||||
func (c *Credentials) AddAuthzAttribute(a string) {
|
||||
c.groupMembership[a] = true
|
||||
}
|
||||
|
||||
// RemoveAuthzAttribute removes an authorization attribute from the credential.
|
||||
func (c *Credentials) RemoveAuthzAttribute(a string) {
|
||||
if _, ok := c.groupMembership[a]; !ok {
|
||||
return
|
||||
}
|
||||
delete(c.groupMembership, a)
|
||||
}
|
||||
|
||||
// EnableAuthzAttribute toggles an authorization attribute to an enabled state on the credential.
|
||||
func (c *Credentials) EnableAuthzAttribute(a string) {
|
||||
if enabled, ok := c.groupMembership[a]; ok && !enabled {
|
||||
c.groupMembership[a] = true
|
||||
}
|
||||
}
|
||||
|
||||
// DisableAuthzAttribute toggles an authorization attribute to a disabled state on the credential.
|
||||
func (c *Credentials) DisableAuthzAttribute(a string) {
|
||||
if enabled, ok := c.groupMembership[a]; ok && enabled {
|
||||
c.groupMembership[a] = false
|
||||
}
|
||||
}
|
||||
|
||||
// Authorized indicates if the credential has the specified authorizing attribute.
|
||||
func (c *Credentials) Authorized(a string) bool {
|
||||
if enabled, ok := c.groupMembership[a]; ok && enabled {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SessionID returns the credential's session ID.
|
||||
func (c *Credentials) SessionID() string {
|
||||
return c.sessionID
|
||||
}
|
||||
|
||||
// Expired indicates if the credential has expired.
|
||||
func (c *Credentials) Expired() bool {
|
||||
if !c.validUntil.IsZero() && time.Now().UTC().After(c.validUntil) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidUntil returns the credential's valid until date
|
||||
func (c *Credentials) ValidUntil() time.Time {
|
||||
return c.validUntil
|
||||
}
|
||||
|
||||
// Attributes returns the Credentials' attributes map.
|
||||
func (c *Credentials) Attributes() map[string]interface{} {
|
||||
return c.attributes
|
||||
}
|
||||
|
||||
// SetAttribute sets the value of an attribute.
|
||||
func (c *Credentials) SetAttribute(k string, v interface{}) {
|
||||
c.attributes[k] = v
|
||||
}
|
||||
|
||||
// SetAttributes replaces the attributes map with the one provided.
|
||||
func (c *Credentials) SetAttributes(a map[string]interface{}) {
|
||||
c.attributes = a
|
||||
}
|
||||
|
||||
// RemoveAttribute deletes an attribute from the attribute map that has the key provided.
|
||||
func (c *Credentials) RemoveAttribute(k string) {
|
||||
delete(c.attributes, k)
|
||||
}
|
||||
Loaded 100 of 1618 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user