pax_global_header 0000666 0000000 0000000 00000000064 13625372257 0014526 g ustar 00root root 0000000 0000000 52 comment=34a78c2feb471f37ec723e1bc1076cbc80ad357a
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/ 0000775 0000000 0000000 00000000000 13625372257 0020555 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/.gitignore 0000664 0000000 0000000 00000000440 13625372257 0022543 0 ustar 00root root 0000000 0000000 *.keytab
*.cap
*.raw
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/.travis.yml 0000664 0000000 0000000 00000004371 13625372257 0022673 0 ustar 00root root 0000000 0000000 language: go
go:
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- master
go_import_path: gopkg.in/jcmturner/gokrb5.v5
sudo: required
services:
- docker
before_install:
- sudo apt-get install -yq krb5-user
- sudo chmod 666 /etc/krb5.conf
- go get github.com/golang/lint/golint
- 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
- docker run -d -h kdc.test.gokrb5 -v /etc/localtime:/etc/localtime:ro -p 88:88 -p 88:88/udp -p 464:464 -p 464:464/udp --name krb5kdc jcmturner/gokrb5:kdc-centos-default
- docker run -d -h kdc.test.gokrb5 -v /etc/localtime:/etc/localtime:ro -p 78:88 -p 78:88/udp --name krb5kdc-old jcmturner/gokrb5:kdc-older
- docker run -d -h kdc.test.gokrb5 -v /etc/localtime:/etc/localtime:ro -p 98:88 -p 98:88/udp --name krb5kdc-latest jcmturner/gokrb5:kdc-latest
- docker run -d -h kdc.resdom.gokrb5 -v /etc/localtime:/etc/localtime:ro -p 188:88 -p 188:88/udp --name krb5kdc-resdom jcmturner/gokrb5:kdc-resdom
- docker run -d -h kdc.test.gokrb5 -v /etc/localtime:/etc/localtime:ro -p 58:88 -p 58:88/udp --name krb5kdc-shorttickets jcmturner/gokrb5:kdc-shorttickets
- docker run -d --add-host host.test.gokrb5:127.0.0.88 -v /etc/localtime:/etc/localtime:ro -p 80:80 -p 443:443 --name gokrb5-http jcmturner/gokrb5:http
before_script:
- GO_FILES=$(find . -iname '*.go' -type f | grep -v /vendor/)
- sudo sed -i 's/nameserver .*/nameserver 127.0.0.1/g' /etc/resolv.conf
env:
- TEST_KDC_ADDR=127.0.0.1 TEST_HTTP_URL="http://host.test.gokrb5/index.html" DNSUTILS_OVERRIDE_NS="127.0.0.1:53" DEBIAN_FRONTEND=noninteractive
script:
- test -z $(gofmt -s -d -l -e $GO_FILES | tee /dev/fd/2 | xargs | sed 's/\s//g') # Fail if a .go file hasn't been formatted with gofmt
- go vet ./... # go vet is the official Go static analyzer
#- golint -set_exit_status $(go list ./...) # golint to be added
- go test -v -race -tags="integration dns" ./... # Run all the tests with the race detector enabled and integration tests
- GOARCH=386 go test -v -tags="integration dns" ./... # 32bit tests
- go run -tags="examples" examples/example.go
addons:
hosts:
- host.test.gokrb5
- kdc.test.gokrb5
- kdc.resdom.gokrb5
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/CONTRIBUTING.md 0000664 0000000 0000000 00000011770 13625372257 0023014 0 ustar 00root root 0000000 0000000 # Contribution Guide to gokrb5
If you're reading this then I guess you are interested in contributing to gokrb5 which is brilliant!
Thank you for your interest and taking the time to read this guide.
The information below should help you successfully contribute and by following these guidelines you are expressing the
respect you have for the project and other contributors.
In return I will endeavour to respond promptly to issues and pull requests but please have patience as I do this in my
spare time. Therefore it can take a few weeks for me to get round to some items.
A variety of contribution types are welcome:
* Raising bug reports.
* Suggesting enhancements.
* Updating and adding to documentation.
* Fixing bugs.
* Coding enhancements.
* Extending test coverage.
This project is not intended to be a support forum for general Kerberos issues and troubleshooting specific environment
configurations.
## Ground Rules
Above all, be respectful and considerate of others. This means:
* Assume miss-communication is mistake not malice.
* Be patient. Many of those contributing are doing this out of good will and in their own time.
If something is really important to you, describe why and what the impact is so that others can understand and relate to
it.
* Provide feedback in a constructive manner.
### Code Contribution Ground Rules
When contributing code please adhere to these responsibilities:
* Create issues (if one does not already exist) for any changes and enhancements that you wish to make.
Discuss how you intend to fix the bug or implement the enhancement to give the community a chance to comment and get us
to the best solution.
* Do not create any new packages unless absolutely necessary.
* Do not alter the existing exported functions and constants unless absolutely necessary.
This would require a major (vX.\_.\_) version update.
* Only add new exported functions and constants if absolutely necessary.
This would require a minor (v\_.X.\_) version update.
* Keep your code platform agnostic.
* Ensure that any functions added or updated are covered by tests.
* Ensure tests pass.
* Ensure godoc comments are created or updated as required for any new or updated code.
* Ensure your contributions are formatted correctly with gofmt. The travis build will test this.
* Do not use external package dependencies.
As gokrb5 is designed to be a core library used in other applications it is best to avoid dependencies the project has
no control over, other than the Go standard library, as issues with any dependency could have large knock on effects.
* Provide useful commit messages.
* Pull requests must address one issue only and keep to the scope of the issue. This makes it easier to review and merge, so your contribution will get
incorporated faster this way.
* Pull requests must have a message obeying this format:
```
More detailed explanatory text. This should reference the related issue.
```
This to adhere to the [git best practice](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project) and
mirror the [contribution guidelines for the Go standard libarary](https://golang.org/doc/contribute.html).
An Example:
```
update to the godoc comments for the function Blah
The godoc comments to function subpkg.Blah have been updated to make it
clearer as to what the function is for.
```
### Issue Raising Ground Rules
A good rule of thumb: The easier you make it for the reader of an issue to help the more help you'll get.
#### Bugs
When raising bugs please include the following items in your issue:
* The version of gokrb5 being used (vX.Y.Z or master or branch name).
* The version of Go being used (output of the ```go version``` command is handy).
* Details of the environment in which you are seeing the issue. For example, what is being used as the KDC,
what the krb5.conf contains, etc.
* Details on how to re-create the issue.
* Details on what you are experiencing that indicates the issue.
* What you expected to see.
* In which gokrb5 package(s) you think the issue arises from.
* If the bug relates to compliance with an RFC please specify the RFC number and section you are referring to.
#### Enhancements
When raising enhancement requests or suggestions please include the following:
* What the enhancment is or would do.
* Why you need the enhancement or why you think it would be a good idea.
* Any suggestions you may have on how to implement.
## Tips
### Running Tests
Running the tests without any particular switches runs only the unit tests.
It is recommended to run tests with the ```-race``` argument.
There are integration tests that run against various other network services such as KDCs, HTTP web servers, DNS servers,
etc. To run these pass ```-tags=integration``` as an argument to the go test command.
There are vagrant and docker resources available to spin up these network services. See the
[readme](https://github.com/jcmturner/gokrb5/blob/master/testenv/README.md) in the testenv directory for instructions.
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/LICENSE 0000664 0000000 0000000 00000026135 13625372257 0021571 0 ustar 00root root 0000000 0000000 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.
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/README.md 0000664 0000000 0000000 00000032200 13625372257 0022031 0 ustar 00root root 0000000 0000000 # gokrb5
[](https://godoc.org/gopkg.in/jcmturner/gokrb5.v5) [](https://goreportcard.com/report/gopkg.in/jcmturner/gokrb5.v5) [](https://travis-ci.org/jcmturner/gokrb5)
To get the package, execute:
```
go get -d gopkg.in/jcmturner/gokrb5.v5/...
```
To import this package, add the following line to your code:
```go
import "gopkg.in/jcmturner/gokrb5.v5/"
```
## Features
* Pure Go - no dependency on external libraries
* No platform specific code
* Server Side
* HTTP handler wrapper implements SPNEGO Kerberos authentication
* HTTP handler wrapper decodes Microsoft AD PAC authorization data
* Client Side
* Client that can authenticate to an SPNEGO Kerberos authenticated web service
* Ability to change client's password
* General
* Kerberos libraries for custom integration
* Parsing Keytab files
* Parsing krb5.conf files
* Parsing client credentials cache files such as `/tmp/krb5cc_$(id -u $(whoami))`
#### Implemented Encryption & Checksum Types
| Implementation | Encryption ID | Checksum ID | RFC |
|-------|-------------|------------|------|
| des3-cbc-sha1-kd | 16 | 12 | 3961 |
| aes128-cts-hmac-sha1-96 | 17 | 15 | 3962 |
| aes256-cts-hmac-sha1-96 | 18 | 16 | 3962 |
| aes128-cts-hmac-sha256-128 | 19 | 19 | 8009 |
| aes256-cts-hmac-sha384-192 | 20 | 20 | 8009 |
| rc4-hmac | 23 | -138 | 4757 |
Currently the following is working/tested:
* Tested against MIT KDC (1.6.3 is the oldest version tested against) and Microsoft Active Directory (Windows 2008 R2)
* Tested against a KDC that supports PA-FX-FAST.
* Tested against users that have pre-authentication required using PA-ENC-TIMESTAMP.
* Microsoft PAC Authorization Data is processed and exposed in the HTTP request context. Available if Microsoft Active Directory is used as the KDC.
## Contributing
If you are interested in contributing to gokrb5, great! Please read the [contribution guidelines](https://github.com/jcmturner/gokrb5/blob/master/CONTRIBUTING.md).
## Usage
---
### Configuration
The gokrb5 libraries use the same krb5.conf configuration file format as MIT Kerberos, described [here](https://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html).
Config instances can be created by loading from a file path or by passing a string, io.Reader or bufio.Scanner to the relevant method:
```go
import "gopkg.in/jcmturner/gokrb5.v5/config"
cfg, err := config.Load("/path/to/config/file")
cfg, err := config.NewConfigFromString(krb5Str) //String must have appropriate newline separations
cfg, err := config.NewConfigFromReader(reader)
cfg, err := config.NewConfigFromScanner(scanner)
```
### Keytab files
Standard keytab files can be read from a file or from a slice of bytes:
```go
import "gopkg.in/jcmturner/gokrb5.v5/keytab"
ktFromFile, err := keytab.Load("/path/to/file.keytab")
ktFromBytes, err := keytab.Parse(b)
```
---
### Kerberos Client
Create a client instance with either a password or a keytab:
```go
import "gopkg.in/jcmturner/gokrb5.v5/client"
cl := client.NewClientWithPassword("username", "REALM.COM", "password")
cl := client.NewClientWithKeytab("username", "REALM.COM", kt)
```
Provide configuration to the client:
```go
cl.WithConfig(cfg)
```
Login:
```go
err := cl.Login()
```
Kerberos Ticket Granting Tickets (TGT) will be automatically renewed unless the client was created from a CCache.
A client can be destroyed with the following method:
```go
cl.Destroy()
```
#### Active Directory KDC and FAST negotiation
Active Directory does not commonly support FAST negotiation so you will need to disable this on the client.
If this is the case you will see this error:
```KDC did not respond appropriately to FAST negotiation```
To resolve this disable PA-FX-Fast on the client before performing Login() with the line below.
```go
cl.GoKrb5Conf.DisablePAFXFast = true
```
#### Authenticate to a Service
##### HTTP SPNEGO
Create the HTTP request object and then call the client's SetSPNEGOHeader method passing the Service Principal Name (SPN) or to auto generate the SPN from the request object pass a null string ""
```go
r, _ := http.NewRequest("GET", "http://host.test.gokrb5/index.html", nil)
spn := ""
cl.SetSPNEGOHeader(r, spn)
HTTPResp, err := http.DefaultClient.Do(r)
```
##### Generic Kerberos Client
To authenticate to a service a client will need to request a service ticket for a Service Principal Name (SPN) and form into an AP_REQ message along with an authenticator encrypted with the session key that was delivered from the KDC along with the service ticket.
The steps below outline how to do this.
* Get the service ticket and session key for the service the client is authenticating to.
The following method will use the client's cache either returning a valid cached ticket, renewing a cached ticket with the KDC or requesting a new ticket from the KDC.
Therefore the GetServiceTicket method can be continually used for the most efficient interaction with the KDC.
```go
tkt, key, err := cl.GetServiceTicket("HTTP/host.test.gokrb5")
```
The steps after this will be specific to the application protocol but it will likely involve a client/server Authentication Protocol exchange (AP exchange).
This will involve these steps:
* Generate a new Authenticator and generate a sequence number and subkey:
```go
auth, _ := types.NewAuthenticator(cl.Credentials.Realm, cl.Credentials.CName)
etype, _ := crypto.GetEtype(key.KeyType)
auth.GenerateSeqNumberAndSubKey(key.KeyType, etype.GetKeyByteSize())
```
* Set the checksum on the authenticator
The checksum is an application specific value. Set as follows:
```go
auth.Cksum = types.Checksum{
CksumType: checksumIDint,
Checksum: checksumBytesSlice,
}
```
* Create the AP_REQ:
```go
APReq, err := messages.NewAPReq(tkt, key, auth)
```
Now send the AP_REQ to the service. How this is done will be specific to the application use case.
#### Changing a Client Password
This feature uses the Microsoft Kerberos Password Change protocol (RFC 3244).
This is implemented in Microsoft Active Directory and in MIT krb5kdc as of version 1.7.
Typically the kpasswd server listens on port 464.
Below is example code for how to use this feature:
```go
cfg, err := config.Load("/path/to/config/file")
if err != nil {
panic(err.Error())
}
kt, err := keytab.Load("/path/to/file.keytab")
if err != nil {
panic(err.Error())
}
cl := client.NewClientWithKeytab("username", "REALM.COM", kt)
cl.WithConfig(cfg)
ok, err := cl.ChangePasswd("newpassword")
if err != nil {
panic(err.Error())
}
if !ok {
panic("failed to change password")
}
```
The client kerberos config (krb5.conf) will need to have either the kpassd_server or admin_server defined in the relevant [realms] section.
For example:
```
REALM.COM = {
kdc = 127.0.0.1:88
kpasswd_server = 127.0.0.1:464
default_domain = realm.com
}
```
See https://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html#realms for more information.
---
### Kerberised Service
#### SPNEGO/Kerberos HTTP Service
A HTTP handler wrapper can be used to implement Kerberos SPNEGO authentication for web services.
To configure the wrapper the keytab for the SPN and a Logger are required:
```go
kt, err := keytab.Load("/path/to/file.keytab")
l := log.New(os.Stderr, "GOKRB5 Service: ", log.Ldate|log.Ltime|log.Lshortfile)
```
Create a handler function of the application's handling method (apphandler in the example below):
```go
h := http.HandlerFunc(apphandler)
```
Configure the HTTP handler:
```go
serviceAccountName = ""
http.Handler("/", service.SPNEGOKRB5Authenticate(h, kt, serviceAccountName, l))
```
The serviceAccountName needs to be defined when using Active Directory where the SPN is mapped to a user account.
If this is not required it should be set to an empty string "".
If authentication succeeds then the request's context will have the following values added so they can be accessed within the application's handler:
* service.CTXKeyAuthenticated - Boolean indicating if the user is authenticated. Use of this value should also handle that this value may not be set and should assume "false" in that case.
* service.CTXKeyCredentials - The authenticated user's credentials.
If Microsoft Active Directory is used as the KDC then additional ADCredentials are available in the credentials.Attributes map under the key credentials.AttributeKeyADCredentials. For example the SIDs of the users group membership are available and can be used by your application for authorization.
Access the credentials within your application:
```go
ctx := r.Context()
if validuser, ok := ctx.Value(service.CTXKeyAuthenticated).(bool); ok && validuser {
if creds, ok := ctx.Value(service.CTXKeyCredentials).(credentials.Credentials); ok {
if ADCreds, ok := creds.Attributes[credentials.AttributeKeyADCredentials].(credentials.ADCredentials); ok {
// Now access the fields of the ADCredentials struct. For example:
groupSids := ADCreds.GroupMembershipSIDs
}
}
}
```
#### Generic Kerberised Service - Validating Client Details
To validate the AP_REQ sent by the client on the service side call this method:
```go
import "gopkg.in/jcmturner/gokrb5.v5/service"
var ktprinc string //The SPN of the service to find the key in the keytab.
var requireHostAddr bool //Whether to force requiring the ticket to contain host addresses to check the client against.
if ok, creds, err := service.ValidateAPREQ(mt.APReq, kt, ktprinc, r.RemoteAddr, requireHostAddr); ok {
// Perform application specifc actions
// creds object has details about the client identity
}
```
---
## References
* [RFC 3244 Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols](https://tools.ietf.org/html/rfc3244)
* [RFC 4120 The Kerberos Network Authentication Service (V5)](https://tools.ietf.org/html/rfc4120)
* [RFC 3961 Encryption and Checksum Specifications for Kerberos 5](https://tools.ietf.org/html/rfc3961)
* [RFC 3962 Advanced Encryption Standard (AES) Encryption for Kerberos 5](https://tools.ietf.org/html/rfc3962)
* [RFC 4121 The Kerberos Version 5 GSS-API Mechanism](https://tools.ietf.org/html/rfc4121)
* [RFC 4178 The Simple and Protected Generic Security Service Application Program Interface (GSS-API) Negotiation Mechanism](https://tools.ietf.org/html/rfc4178.html)
* [RFC 4559 SPNEGO-based Kerberos and NTLM HTTP Authentication in Microsoft Windows](https://tools.ietf.org/html/rfc4559.html)
* [RFC 4757 The RC4-HMAC Kerberos Encryption Types Used by Microsoft Windows](https://tools.ietf.org/html/rfc4757)
* [RFC 6806 Kerberos Principal Name Canonicalization and Cross-Realm Referrals](https://tools.ietf.org/html/rfc6806.html)
* [RFC 6113 A Generalized Framework for Kerberos Pre-Authentication](https://tools.ietf.org/html/rfc6113.html)
* [RFC 8009 AES Encryption with HMAC-SHA2 for Kerberos 5](https://tools.ietf.org/html/rfc8009)
* [IANA Assigned Kerberos Numbers](http://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml)
* [HTTP-Based Cross-Platform Authentication by Using the Negotiate Protocol - Part 1](https://msdn.microsoft.com/en-us/library/ms995329.aspx)
* [HTTP-Based Cross-Platform Authentication by Using the Negotiate Protocol - Part 2](https://msdn.microsoft.com/en-us/library/ms995330.aspx)
* [Microsoft PAC Validation](https://blogs.msdn.microsoft.com/openspecification/2009/04/24/understanding-microsoft-kerberos-pac-validation/)
* [Microsoft Kerberos Protocol Extensions](https://msdn.microsoft.com/en-us/library/cc233855.aspx)
* [Windows Data Types](https://msdn.microsoft.com/en-us/library/cc230273.aspx)
### Useful Links
* https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
## Thanks
* Greg Hudson from the MIT Consortium for Kerberos and Internet Trust for providing useful advice.
## Contributing
Thank you for your interest in contributing to gokrb5 please read the
[contribution guide](https://github.com/jcmturner/gokrb5/blob/master/CONTRIBUTING.md) as it should help you get started.
## Known Issues
| Issue | Worked around? | References |
|-------|-------------|------------|
| The Go standard library's encoding/asn1 package cannot unmarshal into slice of asn1.RawValue | Yes | https://github.com/golang/go/issues/17321 |
| The Go standard library's encoding/asn1 package cannot marshal into a GeneralString | Yes - using https://github.com/jcmturner/gofork/tree/master/encoding/asn1 | https://github.com/golang/go/issues/18832 |
| The Go standard library's encoding/asn1 package cannot marshal into slice of strings and pass stringtype parameter tags to members | Yes - using https://github.com/jcmturner/gofork/tree/master/encoding/asn1 | https://github.com/golang/go/issues/18834 |
| The Go standard library's encoding/asn1 package cannot marshal with application tags | Yes | |
| The Go standard library's x/crypto/pbkdf2.Key function uses the int type for iteraction count limiting meaning the 4294967296 count specified in https://tools.ietf.org/html/rfc3962 section 4 cannot be met on 32bit systems | Yes - using https://github.com/jcmturner/gofork/tree/master/x/crypto/pbkdf2 | https://go-review.googlesource.com/c/crypto/+/85535 |
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/asn1tools/ 0000775 0000000 0000000 00000000000 13625372257 0022500 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/asn1tools/tools.go 0000664 0000000 0000000 00000005701 13625372257 0024172 0 ustar 00root root 0000000 0000000 // 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
}
*/
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/ 0000775 0000000 0000000 00000000000 13625372257 0022033 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/ASExchange.go 0000664 0000000 0000000 00000013003 13625372257 0024325 0 ustar 00root root 0000000 0000000 package client
import (
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/crypto/etype"
"gopkg.in/jcmturner/gokrb5.v5/iana/errorcode"
"gopkg.in/jcmturner/gokrb5.v5/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v5/iana/patype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/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 preformed")
}
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:
// From now on assume this client will need to do this pre-auth and set the PAData
cl.GoKrb5Conf.AssumePAEncTimestampRequired = 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.IsValid(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
}
func setPAData(cl *Client, krberr messages.KRBError, ASReq *messages.ASReq) error {
if !cl.GoKrb5Conf.DisablePAFXFast {
pa := types.PAData{PADataType: patype.PA_REQ_ENC_PA_REP}
ASReq.PAData = append(ASReq.PAData, pa)
}
if cl.GoKrb5Conf.AssumePAEncTimestampRequired {
paTSb, err := types.GetPAEncTSEncAsnMarshalled()
if err != nil {
return krberror.Errorf(err, krberror.KRBMsgError, "error creating PAEncTSEnc for Pre-Authentication")
}
var et etype.EType
if krberr.ErrorCode == 0 {
// This is not in response to an error from the KDC. It is preemptive
et, err = crypto.GetEtype(ASReq.ReqBody.EType[0]) // Take the first as preference
if err != nil {
return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
}
} 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")
}
}
key, err := cl.Key(et, krberr)
if err != nil {
return krberror.Errorf(err, krberror.EncryptingError, "error getting key from credentials")
}
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,
}
ASReq.PAData = append(ASReq.PAData, pa)
}
return nil
}
func preAuthEType(krberr messages.KRBError) (etype etype.EType, err error) {
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
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/TGSExchange.go 0000664 0000000 0000000 00000010407 13625372257 0024464 0 ustar 00root root 0000000 0000000 package client
import (
"time"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// TGSExchange performs a TGS exchange to retrieve a ticket to the specified SPN.
// The ticket retrieved is added to the client's cache.
func (cl *Client) TGSExchange(spn types.PrincipalName, kdcRealm string, tkt messages.Ticket, sessionKey types.EncryptionKey, renewal bool, referral int) (tgsReq messages.TGSReq, tgsRep messages.TGSRep, err error) {
//// Check what sessions we have for this SPN.
//// Will get the session to the default realm if one does not exist for requested SPN
//sess, err := cl.GetSessionFromPrincipalName(spn)
//if err != nil {
// return tgsReq, tgsRep, err
//}
tgsReq, err = messages.NewTGSReq(cl.Credentials.CName, kdcRealm, cl.Config, tkt, sessionKey, spn, renewal)
if err != nil {
return tgsReq, tgsRep, krberror.Errorf(err, krberror.KRBMsgError, "TGS Exchange Error: failed to generate a new TGS_REQ")
}
b, err := tgsReq.Marshal()
if err != nil {
return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: failed to generate a new 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")
}
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")
}
// TODO should this check the first element is krbtgt rather than the nametype?
if tgsRep.Ticket.SName.NameType == nametype.KRB_NT_SRV_INST && !tgsRep.Ticket.SName.Equal(spn) {
if referral > 5 {
return tgsReq, tgsRep, krberror.Errorf(err, krberror.KRBMsgError, "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.
if ok, err := tgsRep.IsValid(cl.Config, tgsReq); !ok {
return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: TGS_REP is not valid")
}
cl.AddSession(tgsRep.Ticket, tgsRep.DecryptedEncPart)
realm := tgsRep.Ticket.SName.NameString[1]
referral++
return cl.TGSExchange(spn, realm, tgsRep.Ticket, tgsRep.DecryptedEncPart.Key, false, referral)
}
if ok, err := tgsRep.IsValid(cl.Config, tgsReq); !ok {
return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: TGS_REP is not valid")
}
return tgsReq, tgsRep, nil
}
// GetServiceTicket makes a request to get a service ticket for the SPN specified
// SPN format: / 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)
sess, err := cl.GetSessionFromPrincipalName(princ)
if err != nil {
return tkt, skey, err
}
// Ensure TGT still valid
if time.Now().UTC().After(sess.EndTime) {
_, err := cl.updateSession(sess)
if err != nil {
return tkt, skey, err
}
// Get the session again as it could have been replaced by the update
sess, err = cl.GetSessionFromPrincipalName(princ)
if err != nil {
return tkt, skey, err
}
}
_, tgsRep, err := cl.TGSExchange(princ, sess.Realm, sess.TGT, sess.SessionKey, false, 0)
if err != nil {
return tkt, skey, err
}
cl.Cache.addEntry(
tgsRep.Ticket,
tgsRep.DecryptedEncPart.AuthTime,
tgsRep.DecryptedEncPart.StartTime,
tgsRep.DecryptedEncPart.EndTime,
tgsRep.DecryptedEncPart.RenewTill,
tgsRep.DecryptedEncPart.Key,
)
return tgsRep.Ticket, tgsRep.DecryptedEncPart.Key, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/cache.go 0000664 0000000 0000000 00000005446 13625372257 0023436 0 ustar 00root root 0000000 0000000 package client
import (
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
"strings"
"sync"
"time"
)
// Cache for client tickets.
type Cache struct {
Entries map[string]CacheEntry
mux sync.RWMutex
}
// CacheEntry holds details for a client 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 := strings.Join(tkt.SName.NameString, "/")
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) {
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
_, tgsRep, err := cl.TGSExchange(spn, e.Ticket.Realm, e.Ticket, e.SessionKey, true, 0)
if err != nil {
return e, err
}
e = cl.Cache.addEntry(
tgsRep.Ticket,
tgsRep.DecryptedEncPart.AuthTime,
tgsRep.DecryptedEncPart.StartTime,
tgsRep.DecryptedEncPart.EndTime,
tgsRep.DecryptedEncPart.RenewTill,
tgsRep.DecryptedEncPart.Key,
)
return e, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/client.go 0000664 0000000 0000000 00000016354 13625372257 0023651 0 ustar 00root root 0000000 0000000 // Package client provides a client library and methods for Kerberos 5 authentication.
package client
import (
"errors"
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/config"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/crypto/etype"
"gopkg.in/jcmturner/gokrb5.v5/iana/errorcode"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// Client side configuration and state.
type Client struct {
Credentials *credentials.Credentials
Config *config.Config
GoKrb5Conf *Config
sessions *sessions
Cache *Cache
}
// Config struct holds GoKRB5 specific client configurations.
// Set Disable_PA_FX_FAST to true to force this behaviour off.
// Set Assume_PA_ENC_TIMESTAMP_Required to send the PA_ENC_TIMESTAMP pro-actively rather than waiting for a KRB_ERROR response from the KDC indicating it is required.
type Config struct {
DisablePAFXFast bool
AssumePAEncTimestampRequired bool
}
// 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) Client {
creds := credentials.NewCredentials(username, realm)
return Client{
Credentials: creds.WithPassword(password),
Config: config.NewConfig(),
GoKrb5Conf: &Config{},
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) Client {
creds := credentials.NewCredentials(username, realm)
return Client{
Credentials: creds.WithKeytab(kt),
Config: config.NewConfig(),
GoKrb5Conf: &Config{},
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) (Client, error) {
cl := Client{
Credentials: c.GetClientCredentials(),
Config: config.NewConfig(),
GoKrb5Conf: &Config{},
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
}
// WithConfig sets the Kerberos configuration for the client.
func (cl *Client) WithConfig(cfg *config.Config) *Client {
cl.Config = cfg
return cl
}
// WithKeytab adds a keytab to the client
func (cl *Client) WithKeytab(kt keytab.Keytab) *Client {
cl.Credentials.WithKeytab(kt)
return cl
}
// WithPassword adds a password to the client
func (cl *Client) WithPassword(password string) *Client {
cl.Credentials.WithPassword(password)
return cl
}
// Key returns a key for the client. Preferably from a keytab and then generated from the password.
// The KRBError would have been returned from the KDC and must be of type KDC_ERR_PREAUTH_REQUIRED.
// If a KRBError is not available pass messages.KRBError{} and a key will be returned from the credentials keytab.
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.NameString, cl.Credentials.Realm, 0, etype.GetETypeID())
} else if cl.Credentials.HasPassword() {
if 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.Realm, etype.GetETypeID(), types.PADataSequence{})
return key, err
}
return types.EncryptionKey{}, errors.New("credential has neither keytab or password to generate key")
}
// LoadConfig loads the Kerberos configuration for the client from file path specified.
func (cl *Client) LoadConfig(cfgPath string) (*Client, error) {
cfg, err := config.Load(cfgPath)
if err != nil {
return cl, err
}
cl.Config = cfg
return cl, nil
}
// 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.Realm == "" {
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() {
sess, err := cl.GetSessionFromRealm(cl.Credentials.Realm)
if err != nil || sess.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.Realm {
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
}
ASReq, err := messages.NewASReqForTGT(cl.Credentials.Realm, cl.Config, cl.Credentials.CName)
if err != nil {
return krberror.Errorf(err, krberror.KRBMsgError, "error generating new AS_REQ")
}
err = setPAData(cl, messages.KRBError{}, &ASReq)
if err != nil {
return krberror.Errorf(err, krberror.KRBMsgError, "failed setting AS_REQ PAData")
}
ASRep, err := cl.ASExchange(cl.Credentials.Realm, ASReq, 0)
if err != nil {
return err
}
cl.AddSession(ASRep.Ticket, ASRep.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.NewCredentials("", "")
cl.sessions.destroy()
cl.Cache.clear()
cl.Credentials = &creds
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/client_ad_integration_test.go 0000664 0000000 0000000 00000012014 13625372257 0027744 0 ustar 00root root 0000000 0000000 // +build adintegration
// To turn on this test use -tags=integration in go test command
package client
import (
"encoding/hex"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/config"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"testing"
)
func TestClient_SuccessfulLogin_AD(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
c.Realms[0].KDC = []string{testdata.TEST_KDC_AD}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("Error on login: %v\n", err)
}
}
func TestClient_GetServiceTicket_AD(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
c.Realms[0].KDC = []string{testdata.TEST_KDC_AD}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("Error on login: %v\n", err)
}
spn := "HTTP/host.test.gokrb5"
tkt, key, err := cl.GetServiceTicket(spn)
if err != nil {
t.Fatalf("Error getting service ticket: %v\n", err)
}
assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
assert.Equal(t, int32(18), key.KeyType)
}
func TestClient_SuccessfulLogin_AD_TRUST_USER_DOMAIN(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_USERKRB5_AD_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
c.Realms[0].KDC = []string{testdata.TEST_KDC_AD_TRUST_USER_DOMAIN}
c.LibDefaults.DefaultRealm = "USER.GOKRB5"
cl := NewClientWithKeytab("testuser1", "USER.GOKRB5", kt)
cl.WithConfig(c)
cl.GoKrb5Conf.DisablePAFXFast = true
err := cl.Login()
if err != nil {
t.Fatalf("Error on login: %v\n", err)
}
}
func TestClient_GetServiceTicket_AD_TRUST_USER_DOMAIN(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_USERKRB5_AD_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
c.Realms[0].KDC = []string{testdata.TEST_KDC_AD_TRUST_USER_DOMAIN}
c.LibDefaults.DefaultRealm = "USER.GOKRB5"
c.LibDefaults.Canonicalize = true
cl := NewClientWithKeytab("testuser1", "USER.GOKRB5", kt)
c.LibDefaults.DefaultTktEnctypes = []string{"rc4-hmac"}
c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName["rc4-hmac"]}
c.LibDefaults.DefaultTGSEnctypes = []string{"rc4-hmac"}
c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName["rc4-hmac"]}
cl.WithConfig(c)
cl.GoKrb5Conf.DisablePAFXFast = true
err := cl.Login()
if err != nil {
t.Fatalf("Error on login: %v\n", err)
}
spn := "HTTP/host.res.gokrb5"
tkt, key, err := cl.GetServiceTicket(spn)
if err != nil {
t.Fatalf("Error getting service ticket: %v\n", err)
}
assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
assert.Equal(t, etypeID.ETypesByName["rc4-hmac"], key.KeyType)
b, _ = hex.DecodeString(testdata.SYSHTTP_RESGOKRB5_AD_KEYTAB)
skt, _ := keytab.Parse(b)
err = tkt.DecryptEncPart(skt, "sysHTTP")
if err != nil {
t.Errorf("error decrypting ticket with service keytab: %v", err)
}
isPAC, pac, err := tkt.GetPACType(skt, "sysHTTP")
if err != nil {
t.Errorf("error getting PAC: %v", err)
}
assert.True(t, isPAC, "Did not find PAC in service ticket")
assert.Equal(t, "testuser1", pac.KerbValidationInfo.EffectiveName.Value, "PAC value not parsed")
}
func TestClient_GetServiceTicket_AD_USER_DOMAIN(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_USERKRB5_AD_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
c.Realms[0].KDC = []string{testdata.TEST_KDC_AD_TRUST_USER_DOMAIN}
c.LibDefaults.DefaultRealm = "USER.GOKRB5"
c.LibDefaults.Canonicalize = true
cl := NewClientWithKeytab("testuser1", "USER.GOKRB5", kt)
c.LibDefaults.DefaultTktEnctypes = []string{"rc4-hmac"}
c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName["rc4-hmac"]}
c.LibDefaults.DefaultTGSEnctypes = []string{"rc4-hmac"}
c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName["rc4-hmac"]}
cl.WithConfig(c)
cl.GoKrb5Conf.DisablePAFXFast = true
err := cl.Login()
if err != nil {
t.Fatalf("Error on login: %v\n", err)
}
spn := "HTTP/user2.user.gokrb5"
tkt, _, err := cl.GetServiceTicket(spn)
if err != nil {
t.Fatalf("Error getting service ticket: %v\n", err)
}
assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
//assert.Equal(t, etypeID.ETypesByName["rc4-hmac"], key.KeyType)
b, _ = hex.DecodeString(testdata.TESTUSER2_USERKRB5_AD_KEYTAB)
skt, _ := keytab.Parse(b)
err = tkt.DecryptEncPart(skt, "testuser2")
if err != nil {
t.Errorf("error decrypting ticket with service keytab: %v", err)
}
isPAC, pac, err := tkt.GetPACType(skt, "testuser2")
if err != nil {
t.Errorf("error getting PAC: %v", err)
}
assert.True(t, isPAC, "Did not find PAC in service ticket")
assert.Equal(t, "testuser1", pac.KerbValidationInfo.EffectiveName.Value, "PAC value not parsed")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/client_dns_test.go 0000664 0000000 0000000 00000004011 13625372257 0025537 0 ustar 00root root 0000000 0000000 // +build dns
package client
import (
"encoding/hex"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/config"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"testing"
)
func TestResolveKDC(t *testing.T) {
//ns := os.Getenv("DNSUTILS_OVERRIDE_NS")
//if ns == "" {
// os.Setenv("DNSUTILS_OVERRIDE_NS", testdata.TEST_NS)
//}
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
c.LibDefaults.DNSLookupKDC = true
var cl Client
cl.WithConfig(c)
count, res, err := cl.Config.GetKDCs(c.LibDefaults.DefaultRealm, true)
if err != nil {
t.Errorf("error resolving KDC via DNS TCP: %v", err)
}
assert.Equal(t, 5, count, "Number of SRV records not as expected: %v", res)
assert.Equal(t, count, len(res), "Map size does not match: %v", res)
expected := []string{
"kdc.test.gokrb5:88",
"kdc1a.test.gokrb5:88",
"kdc2a.test.gokrb5:88",
"kdc1b.test.gokrb5:88",
"kdc2b.test.gokrb5:88",
}
for _, s := range expected {
var found bool
for _, v := range res {
if s == v {
found = true
break
}
}
assert.True(t, found, "Record %s not found in results", s)
}
c.LibDefaults.DNSLookupKDC = false
_, res, err = cl.Config.GetKDCs(c.LibDefaults.DefaultRealm, true)
if err != nil {
t.Errorf("error resolving KDCs from config: %v", err)
}
assert.Equal(t, "127.0.0.1:88", res[1], "KDC not read from config as expected")
}
func TestClient_Login_DNSKDCs(t *testing.T) {
//ns := os.Getenv("DNSUTILS_OVERRIDE_NS")
//if ns == "" {
// os.Setenv("DNSUTILS_OVERRIDE_NS", testdata.TEST_NS)
//}
c, _ := config.NewConfigFromString(testdata.TEST_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{}
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Errorf("error on logging in using DNS lookup of KDCs: %v\n", err)
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/client_integration_test.go 0000664 0000000 0000000 00000052604 13625372257 0027311 0 ustar 00root root 0000000 0000000 // +build integration
// To turn on this test use -tags=integration in go test command
package client
import (
"bytes"
"encoding/hex"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"os/user"
"runtime"
"testing"
"time"
"errors"
"fmt"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/config"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"strings"
"sync"
)
func TestClient_SuccessfulLogin_Keytab(t *testing.T) {
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
var tests = []string{
testdata.TEST_KDC,
testdata.TEST_KDC_OLD,
testdata.TEST_KDC_LASTEST,
}
for _, test := range tests {
c.Realms[0].KDC = []string{addr + ":" + test}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Errorf("error on logging in with KDC %s: %v\n", test, err)
}
}
}
func TestClient_SuccessfulLogin_Password(t *testing.T) {
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
var tests = []string{
testdata.TEST_KDC,
testdata.TEST_KDC_OLD,
testdata.TEST_KDC_LASTEST,
}
for _, test := range tests {
c.Realms[0].KDC = []string{addr + ":" + test}
cl := NewClientWithPassword("testuser1", "TEST.GOKRB5", "passwordvalue")
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Errorf("error on logging in with KDC %s: %v\n", test, err)
}
}
}
func TestClient_SuccessfulLogin_TCPOnly(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
c.LibDefaults.UDPPreferenceLimit = 1
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("error on login: %v\n", err)
}
}
func TestClient_ASExchange_TGSExchange_EncTypes_Keytab(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_LASTEST}
var tests = []string{
"des3-cbc-sha1-kd",
"aes128-cts-hmac-sha1-96",
"aes256-cts-hmac-sha1-96",
"aes128-cts-hmac-sha256-128",
"aes256-cts-hmac-sha384-192",
"rc4-hmac",
}
for _, test := range tests {
c.LibDefaults.DefaultTktEnctypes = []string{test}
c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName[test]}
c.LibDefaults.DefaultTGSEnctypes = []string{test}
c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName[test]}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Errorf("error on login using enctype %s: %v\n", test, err)
}
tkt, key, err := cl.GetServiceTicket("HTTP/host.test.gokrb5")
if err != nil {
t.Errorf("error in TGS exchange using enctype %s: %v", test, err)
}
assert.Equal(t, "TEST.GOKRB5", tkt.Realm, "Realm in ticket not as expected for %s test", test)
assert.Equal(t, etypeID.ETypesByName[test], key.KeyType, "Key is not for enctype %s", test)
}
}
func TestClient_ASExchange_TGSExchange_EncTypes_Password(t *testing.T) {
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_LASTEST}
var tests = []string{
"des3-cbc-sha1-kd",
"aes128-cts-hmac-sha1-96",
"aes256-cts-hmac-sha1-96",
"aes128-cts-hmac-sha256-128",
"aes256-cts-hmac-sha384-192",
"rc4-hmac",
}
for _, test := range tests {
c.LibDefaults.DefaultTktEnctypes = []string{test}
c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName[test]}
c.LibDefaults.DefaultTGSEnctypes = []string{test}
c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName[test]}
cl := NewClientWithPassword("testuser1", "TEST.GOKRB5", "passwordvalue")
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Errorf("error on login using enctype %s: %v\n", test, err)
}
tkt, key, err := cl.GetServiceTicket("HTTP/host.test.gokrb5")
if err != nil {
t.Errorf("error in TGS exchange using enctype %s: %v", test, err)
}
assert.Equal(t, "TEST.GOKRB5", tkt.Realm, "Realm in ticket not as expected for %s test", test)
assert.Equal(t, etypeID.ETypesByName[test], key.KeyType, "Key is not for enctype %s", test)
}
}
func TestClient_FailedLogin(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_WRONGPASSWD)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err == nil {
t.Fatal("login with incorrect password did not error")
}
}
func TestClient_SuccessfulLogin_UserRequiringPreAuth(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("error on login: %v\n", err)
}
}
func TestClient_SuccessfulLogin_UserRequiringPreAuth_TCPOnly(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
c.LibDefaults.UDPPreferenceLimit = 1
cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("error on login: %v\n", err)
}
}
func TestClient_NetworkTimeout(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
c.Realms[0].KDC = []string{testdata.TEST_KDC_BADADDR + ":88"}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err == nil {
t.Fatal("login with incorrect KDC address did not error")
}
}
func TestClient_GetServiceTicket(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("error on login: %v\n", err)
}
spn := "HTTP/host.test.gokrb5"
tkt, key, err := cl.GetServiceTicket(spn)
if err != nil {
t.Fatalf("error getting service ticket: %v\n", err)
}
assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
assert.Equal(t, int32(18), key.KeyType)
//Check cache use - should get the same values back again
tkt2, key2, err := cl.GetServiceTicket(spn)
if err != nil {
t.Fatalf("error getting service ticket: %v\n", err)
}
assert.Equal(t, tkt.EncPart.Cipher, tkt2.EncPart.Cipher)
assert.Equal(t, key.KeyValue, key2.KeyValue)
}
func TestClient_GetServiceTicket_InvalidSPN(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("error on login: %v\n", err)
}
spn := "host.test.gokrb5"
_, _, err = cl.GetServiceTicket(spn)
assert.NotNil(t, err, "Expected unknown principal error")
assert.True(t, strings.Contains(err.Error(), "KDC_ERR_S_PRINCIPAL_UNKNOWN"), "Error text not as expected")
}
func TestClient_GetServiceTicket_OlderKDC(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_OLD}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("error on login: %v\n", err)
}
spn := "HTTP/host.test.gokrb5"
tkt, key, err := cl.GetServiceTicket(spn)
if err != nil {
t.Fatalf("error getting service ticket: %v\n", err)
}
assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
assert.Equal(t, int32(18), key.KeyType)
}
func TestClient_SetSPNEGOHeader(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("error on AS_REQ: %v\n", err)
}
url := os.Getenv("TEST_HTTP_URL")
if url == "" {
url = testdata.TEST_HTTP_URL
}
r, _ := http.NewRequest("GET", url, nil)
httpResp, err := http.DefaultClient.Do(r)
if err != nil {
t.Fatalf("request error: %v\n", err)
}
assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected")
err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
if err != nil {
t.Fatalf("error setting client SPNEGO header: %v", err)
}
httpResp, err = http.DefaultClient.Do(r)
if err != nil {
t.Fatalf("request error: %v\n", err)
}
assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected")
}
func TestMultiThreadedClientUse(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func() {
defer wg.Done()
err := cl.Login()
if err != nil {
panic(err)
}
}()
}
wg.Wait()
var wg2 sync.WaitGroup
wg2.Add(5)
for i := 0; i < 5; i++ {
go func() {
defer wg2.Done()
err := spnegoGet(&cl)
if err != nil {
panic(err)
}
}()
}
wg2.Wait()
}
func TestMultiThreadedClientSession(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("failed to log in: %v", err)
}
s, err := cl.GetSessionFromRealm("TEST.GOKRB5")
if err != nil {
t.Fatalf("error initially getting session: %v", err)
}
go func() {
for {
err := cl.renewTGT(s)
if err != nil {
t.Logf("error renewing TGT: %v", err)
}
time.Sleep(time.Millisecond * 100)
}
}()
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
s, err := cl.GetSessionFromRealm("TEST.GOKRB5")
if err != nil {
t.Logf("error getting session: %v", err)
}
fmt.Fprintf(ioutil.Discard, "%v", s.RenewTill)
}()
time.Sleep(time.Second)
}
wg.Wait()
}
func spnegoGet(cl *Client) error {
url := os.Getenv("TEST_HTTP_URL")
if url == "" {
url = testdata.TEST_HTTP_URL
}
r, _ := http.NewRequest("GET", url, nil)
httpResp, err := http.DefaultClient.Do(r)
if err != nil {
return fmt.Errorf("request error: %v\n", err)
}
if httpResp.StatusCode != http.StatusUnauthorized {
return errors.New("did not get unauthorized code when no SPNEGO header set")
}
err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
if err != nil {
return fmt.Errorf("error setting client SPNEGO header: %v", err)
}
httpResp, err = http.DefaultClient.Do(r)
if err != nil {
return fmt.Errorf("request error: %v\n", err)
}
if httpResp.StatusCode != http.StatusOK {
return errors.New("did not get OK code when SPNEGO header set")
}
return nil
}
func TestNewClientFromCCache(t *testing.T) {
b, err := hex.DecodeString(testdata.CCACHE_TEST)
if err != nil {
t.Fatalf("error decoding test data")
}
cc, err := credentials.ParseCCache(b)
if err != nil {
t.Fatal("error getting test CCache")
}
cl, err := NewClientFromCCache(cc)
if err != nil {
t.Fatalf("error creating client from CCache: %v", err)
}
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
cl.WithConfig(c)
if ok, err := cl.IsConfigured(); !ok {
t.Fatalf("client was not configured from CCache: %v", err)
}
}
// Login to the TEST.GOKRB5 domain and request service ticket for resource in the RESDOM.GOKRB5 domain.
// There is a trust between the two domains.
func TestClient_GetServiceTicket_Trusted_Resource_Domain(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
for i, r := range c.Realms {
if r.Realm == "TEST.GOKRB5" {
c.Realms[i].KDC = []string{addr + ":" + testdata.TEST_KDC}
}
if r.Realm == "RESDOM.GOKRB5" {
c.Realms[i].KDC = []string{addr + ":" + testdata.TEST_KDC_RESDOM}
}
}
c.LibDefaults.DefaultRealm = "TEST.GOKRB5"
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
c.LibDefaults.DefaultTktEnctypes = []string{"aes256-cts-hmac-sha1-96"}
c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName["aes256-cts-hmac-sha1-96"]}
c.LibDefaults.DefaultTGSEnctypes = []string{"aes256-cts-hmac-sha1-96"}
c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName["aes256-cts-hmac-sha1-96"]}
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("error on login: %v\n", err)
}
spn := "HTTP/host.resdom.gokrb5"
tkt, key, err := cl.GetServiceTicket(spn)
if err != nil {
t.Fatalf("error getting service ticket: %v\n", err)
}
assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
assert.Equal(t, etypeID.ETypesByName["aes256-cts-hmac-sha1-96"], key.KeyType)
b, _ = hex.DecodeString(testdata.SYSHTTP_RESDOM_KEYTAB)
skt, _ := keytab.Parse(b)
err = tkt.DecryptEncPart(skt, "")
if err != nil {
t.Errorf("error decrypting ticket with service keytab: %v", err)
}
}
const (
kinitCmd = "kinit"
kvnoCmd = "kvno"
spn = "HTTP/host.test.gokrb5"
)
func login() error {
file, err := os.Create("/etc/krb5.conf")
if err != nil {
return fmt.Errorf("cannot open krb5.conf: %v", err)
}
defer file.Close()
fmt.Fprintf(file, testdata.TEST_KRB5CONF)
cmd := exec.Command(kinitCmd, "testuser1@TEST.GOKRB5")
stdinR, stdinW := io.Pipe()
stderrR, stderrW := io.Pipe()
cmd.Stdin = stdinR
cmd.Stderr = stderrW
err = cmd.Start()
if err != nil {
return fmt.Errorf("could not start %s command: %v", kinitCmd, err)
}
go func() {
io.WriteString(stdinW, "passwordvalue")
stdinW.Close()
}()
errBuf := new(bytes.Buffer)
go func() {
io.Copy(errBuf, stderrR)
stderrR.Close()
}()
err = cmd.Wait()
if err != nil {
return fmt.Errorf("%s did not run successfully: %v stderr: %s", kinitCmd, err, string(errBuf.Bytes()))
}
return nil
}
func getServiceTkt() error {
cmd := exec.Command(kvnoCmd, spn)
err := cmd.Start()
if err != nil {
return fmt.Errorf("could not start %s command: %v", kvnoCmd, err)
}
err = cmd.Wait()
if err != nil {
return fmt.Errorf("%s did not run successfully: %v", kvnoCmd, err)
}
return nil
}
func loadCCache() (credentials.CCache, error) {
usr, _ := user.Current()
cpath := "/tmp/krb5cc_" + usr.Uid
return credentials.LoadCCache(cpath)
}
func TestGetServiceTicketFromCCacheTGT(t *testing.T) {
err := login()
if err != nil {
t.Fatalf("error logging in with kinit: %v", err)
}
c, err := loadCCache()
if err != nil {
t.Errorf("error loading CCache: %v", err)
}
cfg, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
cfg.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
cl, err := NewClientFromCCache(c)
if err != nil {
t.Fatalf("error generating client from ccache: %v", err)
}
cl.WithConfig(cfg)
url := os.Getenv("TEST_HTTP_URL")
if url == "" {
url = testdata.TEST_HTTP_URL
}
r, _ := http.NewRequest("GET", url, nil)
err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
if err != nil {
t.Fatalf("error setting client SPNEGO header: %v", err)
}
httpResp, err := http.DefaultClient.Do(r)
if err != nil {
t.Fatalf("request error: %v\n", err)
}
assert.Equal(t, http.StatusOK, httpResp.StatusCode, "status code in response to client SPNEGO request not as expected")
}
func TestGetServiceTicketFromCCacheWithoutKDC(t *testing.T) {
err := login()
if err != nil {
t.Fatalf("error logging in with kinit: %v", err)
}
err = getServiceTkt()
if err != nil {
t.Fatalf("error getting service ticket: %v", err)
}
c, err := loadCCache()
if err != nil {
t.Errorf("error loading CCache: %v", err)
}
cfg, _ := config.NewConfigFromString("...")
cl, err := NewClientFromCCache(c)
if err != nil {
t.Fatalf("error generating client from ccache: %v", err)
}
cl.WithConfig(cfg)
url := os.Getenv("TEST_HTTP_URL")
if url == "" {
url = testdata.TEST_HTTP_URL
}
r, _ := http.NewRequest("GET", url, nil)
err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
if err != nil {
t.Fatalf("error setting client SPNEGO header: %v", err)
}
httpResp, err := http.DefaultClient.Do(r)
if err != nil {
t.Fatalf("request error: %v\n", err)
}
assert.Equal(t, http.StatusOK, httpResp.StatusCode, "status code in response to client SPNEGO request not as expected")
}
func TestClient_ChangePasswd(t *testing.T) {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC}
c.Realms[0].KPasswdServer = []string{addr + ":464"}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
ok, err := cl.ChangePasswd("newpassword")
if err != nil {
t.Fatalf("error changing password: %v", err)
}
assert.True(t, ok, "password was not changed")
cl = NewClientWithPassword("testuser1", "TEST.GOKRB5", "newpassword")
cl.WithConfig(c)
ok, err = cl.ChangePasswd(testdata.TESTUSER1_PASSWORD)
if err != nil {
t.Fatalf("error changing password: %v", err)
}
assert.True(t, ok, "password was not changed back")
}
func TestClient_AutoRenew_Goroutine_Count(t *testing.T) {
// Tests that the auto renew of client credentials is not spawning goroutines out of control.
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_SHORTTICKETS}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Errorf("error on logging in: %v\n", err)
}
n := runtime.NumGoroutine()
for i := 0; i < 6; i++ {
time.Sleep(time.Second * 20)
if runtime.NumGoroutine() > n {
t.Fatalf("number of goroutines is increasing: should not be more than %d, is %d", n, runtime.NumGoroutine())
}
}
}
func TestClient_Destroy(t *testing.T) {
addr := os.Getenv("TEST_KDC_ADDR")
if addr == "" {
addr = testdata.TEST_KDC_ADDR
}
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_SHORTTICKETS}
cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
err := cl.Login()
if err != nil {
t.Fatalf("error on login: %v\n", err)
}
spn := "HTTP/host.test.gokrb5"
_, _, err = cl.GetServiceTicket(spn)
if err != nil {
t.Fatalf("error getting service ticket: %v\n", err)
}
n := runtime.NumGoroutine()
time.Sleep(time.Second * 60)
cl.Destroy()
time.Sleep(time.Second * 5)
assert.True(t, runtime.NumGoroutine() < n, "auto-renewal goroutine was not stopped when client destroyed")
is, _ := cl.IsConfigured()
assert.False(t, is, "client is still configured after it was destroyed")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/http.go 0000664 0000000 0000000 00000002753 13625372257 0023350 0 ustar 00root root 0000000 0000000 package client
import (
"encoding/base64"
"fmt"
"net/http"
"strings"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/gssapi"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// SetSPNEGOHeader gets the service ticket and sets it as the SPNEGO authorization header on HTTP request object.
// To auto generate the SPN from the request object pass a null string "".
func (cl *Client) SetSPNEGOHeader(r *http.Request, spn string) error {
if spn == "" {
spn = "HTTP/" + strings.SplitN(r.Host, ":", 2)[0]
}
tkt, skey, err := cl.GetServiceTicket(spn)
if err != nil {
return fmt.Errorf("could not get service ticket: %v", err)
}
err = SetSPNEGOHeader(*cl.Credentials, tkt, skey, r)
if err != nil {
return err
}
return nil
}
// SetSPNEGOHeader sets the provided ticket as the SPNEGO authorization header on HTTP request object.
func SetSPNEGOHeader(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey, r *http.Request) error {
SPNEGOToken, err := gssapi.GetSPNEGOKrbNegTokenInit(creds, tkt, sessionKey)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "could not generate SPNEGO negotiation token")
}
nb, err := SPNEGOToken.Marshal()
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "could not marshal SPNEGO")
}
hs := "Negotiate " + base64.StdEncoding.EncodeToString(nb)
r.Header.Set("Authorization", hs)
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/network.go 0000664 0000000 0000000 00000012222 13625372257 0024052 0 ustar 00root root 0000000 0000000 package client
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"time"
"gopkg.in/jcmturner/gokrb5.v5/iana/errorcode"
"gopkg.in/jcmturner/gokrb5.v5/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
}
func dialKDCUDP(count int, kdcs map[int]string) (conn *net.UDPConn, err error) {
i := 1
for i <= count {
udpAddr, e := net.ResolveUDPAddr("udp", kdcs[i])
if e != nil {
err = fmt.Errorf("error resolving KDC address: %v", e)
return
}
conn, err = net.DialUDP("udp", nil, udpAddr)
if err == nil {
conn.SetDeadline(time.Now().Add(time.Duration(5 * time.Second)))
return
}
i++
}
err = errors.New("error in getting a UDP connection to any of the KDCs")
return
}
func dialKDCTCP(count int, kdcs map[int]string) (conn *net.TCPConn, err error) {
i := 1
for i <= count {
tcpAddr, e := net.ResolveTCPAddr("tcp", kdcs[i])
if e != nil {
err = fmt.Errorf("error resolving KDC address: %v", e)
return
}
conn, err = net.DialTCP("tcp", nil, tcpAddr)
if err == nil {
conn.SetDeadline(time.Now().Add(time.Duration(5 * time.Second)))
return
}
i++
}
err = errors.New("error in getting a TCP connection to any of the KDCs")
return
}
// Send the bytes to the KDC over 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)
}
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)
}
// Send the bytes 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
}
// Send the bytes 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
NB: network byte order == big endian
*/
var buf bytes.Buffer
binary.Write(&buf, binary.BigEndian, uint32(len(b)))
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
}
func checkForKRBError(b []byte) ([]byte, error) {
var KRBErr messages.KRBError
if err := KRBErr.Unmarshal(b); err == nil {
return b, KRBErr
}
return b, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/passwd.go 0000664 0000000 0000000 00000004432 13625372257 0023666 0 ustar 00root root 0000000 0000000 package client
import (
"fmt"
"net"
"gopkg.in/jcmturner/gokrb5.v5/kadmin"
"gopkg.in/jcmturner/gokrb5.v5/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.Realm, cl.Config, cl.Credentials.CName)
if err != nil {
return false, err
}
ASRep, err := cl.ASExchange(cl.Credentials.Realm, ASReq, 0)
if err != nil {
return false, err
}
msg, key, err := kadmin.ChangePasswdMsg(cl.Credentials.CName, cl.Credentials.Realm, 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 kdamin: %s", r.Result)
}
return true, nil
}
func (cl *Client) sendToKPasswd(msg kadmin.Request) (r kadmin.Reply, err error) {
_, kps, err := cl.Config.GetKpasswdServers(cl.Credentials.Realm, 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
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/session.go 0000664 0000000 0000000 00000012330 13625372257 0024044 0 ustar 00root root 0000000 0000000 package client
import (
"fmt"
"sync"
"time"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// Sessions keyed on the realm name
type sessions struct {
Entries map[string]*session
mux sync.RWMutex
}
func (s *sessions) destroy() {
s.mux.Lock()
defer s.mux.Unlock()
for k, e := range s.Entries {
e.destroy()
delete(s.Entries, k)
}
}
// Client session struct.
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
}
func (s *session) update(tkt 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 = tkt
s.SessionKey = dep.Key
s.SessionKeyExpiration = dep.KeyExpiration
}
func (s *session) destroy() {
s.mux.Lock()
defer s.mux.Unlock()
s.cancel <- true
s.EndTime = time.Now().UTC()
s.RenewTill = s.EndTime
s.SessionKeyExpiration = s.EndTime
}
// 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(tkt messages.Ticket, dep messages.EncKDCRepPart) {
cl.sessions.mux.Lock()
defer cl.sessions.mux.Unlock()
s := &session{
Realm: tkt.SName.NameString[1],
AuthTime: dep.AuthTime,
EndTime: dep.EndTime,
RenewTill: dep.RenewTill,
TGT: tkt,
SessionKey: dep.Key,
SessionKeyExpiration: dep.KeyExpiration,
cancel: make(chan bool, 1),
}
// if a session already exists for this, cancel its auto renew.
if i, ok := cl.sessions.Entries[tkt.SName.NameString[1]]; ok {
i.cancel <- true
}
cl.sessions.Entries[tkt.SName.NameString[1]] = s
cl.enableAutoSessionRenewal(s)
}
// enableAutoSessionRenewal turns on the automatic renewal for the client's TGT session.
func (cl *Client) enableAutoSessionRenewal(s *session) {
var timer *time.Timer
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.updateSession(s)
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 {
spn := types.PrincipalName{
NameType: nametype.KRB_NT_SRV_INST,
NameString: []string{"krbtgt", s.Realm},
}
_, tgsRep, err := cl.TGSExchange(spn, s.TGT.Realm, s.TGT, s.SessionKey, true, 0)
if err != nil {
return krberror.Errorf(err, krberror.KRBMsgError, "error renewing TGT")
}
s.update(tgsRep.Ticket, tgsRep.DecryptedEncPart)
return nil
}
// updateSession updates either through renewal or creating a new login.
// The boolean indicates if the update was a renewal.
func (cl *Client) updateSession(s *session) (bool, error) {
if time.Now().UTC().Before(s.RenewTill) {
err := cl.renewTGT(s)
return true, err
}
err := cl.Login()
return false, err
}
func (cl *Client) getSessionFromRemoteRealm(realm string) (*session, error) {
cl.sessions.mux.RLock()
sess, ok := cl.sessions.Entries[cl.Credentials.Realm]
cl.sessions.mux.RUnlock()
if !ok {
return nil, fmt.Errorf("client does not have a session for realm %s, login first", cl.Credentials.Realm)
}
spn := types.PrincipalName{
NameType: nametype.KRB_NT_SRV_INST,
NameString: []string{"krbtgt", realm},
}
_, tgsRep, err := cl.TGSExchange(spn, cl.Credentials.Realm, sess.TGT, sess.SessionKey, false, 0)
if err != nil {
return nil, err
}
cl.AddSession(tgsRep.Ticket, tgsRep.DecryptedEncPart)
cl.sessions.mux.RLock()
defer cl.sessions.mux.RUnlock()
return cl.sessions.Entries[realm], nil
}
// GetSessionFromRealm returns the session for the realm provided.
func (cl *Client) GetSessionFromRealm(realm string) (sess *session, err error) {
cl.sessions.mux.RLock()
s, ok := cl.sessions.Entries[realm]
cl.sessions.mux.RUnlock()
if !ok {
// Try to request TGT from trusted remote Realm
s, err = cl.getSessionFromRemoteRealm(realm)
if err != nil {
return
}
}
// Create another session to return to prevent race condition.
sess = &session{
Realm: s.Realm,
AuthTime: s.AuthTime,
EndTime: s.EndTime,
RenewTill: s.RenewTill,
TGT: s.TGT,
SessionKey: s.SessionKey,
SessionKeyExpiration: s.SessionKeyExpiration,
}
return
}
// GetSessionFromPrincipalName returns the session for the realm of the principal provided.
func (cl *Client) GetSessionFromPrincipalName(spn types.PrincipalName) (*session, error) {
realm := cl.Config.ResolveRealm(spn.NameString[len(spn.NameString)-1])
return cl.GetSessionFromRealm(realm)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/config/ 0000775 0000000 0000000 00000000000 13625372257 0022022 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/config/hosts.go 0000664 0000000 0000000 00000006517 13625372257 0023522 0 ustar 00root root 0000000 0000000 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
// 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("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 = 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
for _, r := range c.Realms {
if r.Realm == realm {
ks = r.KDC
break
}
}
count = len(ks)
if count < 1 {
return count, kdcs, fmt.Errorf("no KDCs defined in configuration for realm %s", realm)
}
kdcs = randServOrder(ks)
}
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
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/config/krb5conf.go 0000664 0000000 0000000 00000052441 13625372257 0024070 0 ustar 00root root 0000000 0000000 // 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.v5/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 {
line = strings.TrimSpace(line)
if line == "" {
continue
}
if strings.Contains(line, "v4_") {
return errors.New("v4 configurations are not supported in Realms section")
}
if !strings.Contains(line, "=") {
return fmt.Errorf("libdefaults configuration line invalid: %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 fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
l.AllowWeakCrypto = v
case "canonicalize":
v, err := parseBoolean(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
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 fmt.Errorf("libdefaults configuration line invalid: %s", line)
}
l.CCacheType = int(v)
case "clockskew":
d, err := parseDuration(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
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 fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
l.DNSCanonicalizeHostname = v
case "dns_lookup_kdc":
v, err := parseBoolean(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
l.DNSLookupKDC = v
case "dns_lookup_realm":
v, err := parseBoolean(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
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 fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
l.Forwardable = v
case "ignore_acceptor_hostname":
v, err := parseBoolean(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
l.IgnoreAcceptorHostname = v
case "k5login_authoritative":
v, err := parseBoolean(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
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 fmt.Errorf("libdefaults configuration line invalid: %s", line)
}
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 fmt.Errorf("libdefaults configuration line invalid: %s", line)
}
l.KDCTimeSync = int(v)
case "noaddresses":
v, err := parseBoolean(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
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 fmt.Errorf("libdefaults configuration line invalid: %s", line)
}
v = append(v, int(i))
}
l.PreferredPreauthTypes = v
case "proxiable":
v, err := parseBoolean(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
l.Proxiable = v
case "rdns":
v, err := parseBoolean(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
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 fmt.Errorf("libdefaults configuration line invalid: %s", line)
}
l.RealmTryDomains = int(v)
case "renew_lifetime":
d, err := parseDuration(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
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 fmt.Errorf("libdefaults configuration line invalid: %s", line)
}
l.SafeChecksumType = int(v)
case "ticket_lifetime":
d, err := parseDuration(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
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 fmt.Errorf("libdefaults configuration line invalid: %s", line)
}
l.UDPPreferenceLimit = int(v)
case "verify_ap_req_nofail":
v, err := parseBoolean(p[1])
if err != nil {
return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line)
}
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) error {
r.Realm = name
var adminServerFinal bool
var KDCFinal bool
var kpasswdServerFinal bool
var masterKDCFinal bool
for _, line := range lines {
if strings.TrimSpace(line) == "" {
continue
}
if !strings.Contains(line, "=") {
return fmt.Errorf("realm configuration line invalid: %s", line)
}
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 nil
}
// Parse the lines of the [realms] section of the configuration into an slice of Realm structs.
func parseRealms(lines []string) ([]Realm, error) {
var realms []Realm
start := -1
var name string
for i, l := range lines {
if strings.TrimSpace(l) == "" {
continue
}
if strings.Contains(l, "v4_") {
return nil, errors.New("v4 configurations are not supported in Realms section")
}
if strings.Contains(l, "{") {
if start >= 0 {
// already started a block!!!
return nil, errors.New("invalid Realms section in configuration")
}
start = i
if !strings.Contains(l, "=") {
return nil, fmt.Errorf("realm configuration line invalid: %s", l)
}
p := strings.Split(l, "=")
name = strings.TrimSpace(p[0])
}
if strings.Contains(l, "}") {
if start < 0 {
// but not started a block!!!
return nil, errors.New("invalid Realms section in configuration")
}
var r Realm
r.parseLines(name, lines[start+1:i])
realms = append(realms, r)
start = -1
}
}
return realms, nil
}
// 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 {
if strings.TrimSpace(line) == "" {
continue
}
if !strings.Contains(line, "=") {
return fmt.Errorf("realm configuration line invalid: %s", line)
}
p := strings.Split(line, "=")
domain := strings.TrimSpace(strings.ToLower(p[0]))
realm := strings.TrimSpace(strings.ToUpper(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()
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 {
return nil, fmt.Errorf("error processing libdefaults section: %v", err)
}
case "realms":
realms, err := parseRealms(lines[start:end])
if err != nil {
return nil, fmt.Errorf("error processing realms section: %v", err)
}
c.Realms = realms
case "domain_realm":
err := c.DomainRealm.parseLines(lines[start:end])
if err != nil {
return nil, fmt.Errorf("error processing domaain_realm section: %v", err)
}
default:
continue
}
}
return c, nil
}
// 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)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/config/krb5conf_test.go 0000664 0000000 0000000 00000036456 13625372257 0025137 0 ustar 00root root 0000000 0000000 package config
import (
"io/ioutil"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
const (
krb5Conf = `
[logging]
default = FILE:/var/log/kerberos/krb5libs.log
kdc = FILE:/var/log/kerberos/krb5kdc.log
admin_server = FILE:/var/log/kerberos/kadmind.log
[libdefaults]
default_realm = TEST.GOKRB5
dns_lookup_realm = false
dns_lookup_kdc = false
#dns_lookup_kdc = true
;dns_lookup_kdc = true
#dns_lookup_kdc = true
;dns_lookup_kdc = true
ticket_lifetime = 10h
forwardable = yes
default_keytab_name = FILE:/etc/krb5.keytab
default_client_keytab_name = FILE:/home/gokrb5/client.keytab
default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
[realms]
TEST.GOKRB5 = {
kdc = 10.80.88.88:88
kdc = assume.port.num
kdc = some.other.port:1234
kdc = 10.80.88.88*
kdc = 10.1.2.3.4:88
admin_server = 10.80.88.88:749
default_domain = test.gokrb5
}
EXAMPLE.COM = {
kdc = kerberos.example.com
kdc = kerberos-1.example.com
admin_server = kerberos.example.com
auth_to_local = RULE:[1:$1@$0](.*@EXAMPLE.COM)s/.*//
}
[domain_realm]
.test.gokrb5 = TEST.GOKRB5
test.gokrb5 = TEST.GOKRB5
.example.com = EXAMPLE.COM
hostname1.example.com = EXAMPLE.COM
hostname2.example.com = TEST.GOKRB5
[appdefaults]
pam = {
debug = false
ticket_lifetime = 36000
renew_lifetime = 36000
forwardable = true
krb4_convert = false
}
`
krb5Conf2 = `
[logging]
default = FILE:/var/log/kerberos/krb5libs.log
kdc = FILE:/var/log/kerberos/krb5kdc.log
admin_server = FILE:/var/log/kerberos/kadmind.log
[libdefaults]
noaddresses = true
default_realm = TEST.GOKRB5
dns_lookup_realm = false
dns_lookup_kdc = false
#dns_lookup_kdc = true
;dns_lookup_kdc = true
#dns_lookup_kdc = true
;dns_lookup_kdc = true
ticket_lifetime = 10h
forwardable = yes
default_keytab_name = FILE:/etc/krb5.keytab
default_client_keytab_name = FILE:/home/gokrb5/client.keytab
default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
[domain_realm]
.test.gokrb5 = TEST.GOKRB5
test.gokrb5 = TEST.GOKRB5
[appdefaults]
pam = {
debug = false
ticket_lifetime = 36000
renew_lifetime = 36000
forwardable = true
krb4_convert = false
}
[realms]
TEST.GOKRB5 = {
kdc = 10.80.88.88:88
kdc = assume.port.num
kdc = some.other.port:1234
kdc = 10.80.88.88*
kdc = 10.1.2.3.4:88
admin_server = 10.80.88.88:749
default_domain = test.gokrb5
}
EXAMPLE.COM = {
kdc = kerberos.example.com
kdc = kerberos-1.example.com
admin_server = kerberos.example.com
}
`
krb5ConfNoBlankLines = `
[logging]
default = FILE:/var/log/kerberos/krb5libs.log
kdc = FILE:/var/log/kerberos/krb5kdc.log
admin_server = FILE:/var/log/kerberos/kadmind.log
[libdefaults]
default_realm = TEST.GOKRB5
dns_lookup_realm = false
dns_lookup_kdc = false
#dns_lookup_kdc = true
;dns_lookup_kdc = true
#dns_lookup_kdc = true
;dns_lookup_kdc = true
ticket_lifetime = 10h
forwardable = yes
default_keytab_name = FILE:/etc/krb5.keytab
default_client_keytab_name = FILE:/home/gokrb5/client.keytab
default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
[realms]
TEST.GOKRB5 = {
kdc = 10.80.88.88:88
kdc = assume.port.num
kdc = some.other.port:1234
kdc = 10.80.88.88*
kdc = 10.1.2.3.4:88
admin_server = 10.80.88.88:749
default_domain = test.gokrb5
}
EXAMPLE.COM = {
kdc = kerberos.example.com
kdc = kerberos-1.example.com
admin_server = kerberos.example.com
auth_to_local = RULE:[1:$1@$0](.*@EXAMPLE.COM)s/.*//
}
[domain_realm]
.test.gokrb5 = TEST.GOKRB5
test.gokrb5 = TEST.GOKRB5
`
krb5ConfTabs = `
[logging]
default = FILE:/var/log/kerberos/krb5libs.log
kdc = FILE:/var/log/kerberos/krb5kdc.log
admin_server = FILE:/var/log/kerberos/kadmind.log
[libdefaults]
default_realm = TEST.GOKRB5
dns_lookup_realm = false
dns_lookup_kdc = false
#dns_lookup_kdc = true
;dns_lookup_kdc = true
#dns_lookup_kdc = true
;dns_lookup_kdc = true
ticket_lifetime = 10h
forwardable = yes
default_keytab_name = FILE:/etc/krb5.keytab
default_client_keytab_name = FILE:/home/gokrb5/client.keytab
default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
[realms]
TEST.GOKRB5 = {
kdc = 10.80.88.88:88
kdc = assume.port.num
kdc = some.other.port:1234
kdc = 10.80.88.88*
kdc = 10.1.2.3.4:88
admin_server = 10.80.88.88:749
default_domain = test.gokrb5
}
EXAMPLE.COM = {
kdc = kerberos.example.com
kdc = kerberos-1.example.com
admin_server = kerberos.example.com
auth_to_local = RULE:[1:$1@$0](.*@EXAMPLE.COM)s/.*//
}
[domain_realm]
.test.gokrb5 = TEST.GOKRB5
test.gokrb5 = TEST.GOKRB5
.example.com = EXAMPLE.COM
hostname1.example.com = EXAMPLE.COM
hostname2.example.com = TEST.GOKRB5
[appdefaults]
pam = {
debug = false
ticket_lifetime = 36000
renew_lifetime = 36000
forwardable = true
krb4_convert = false
}`
)
func TestLoad(t *testing.T) {
t.Parallel()
cf, _ := ioutil.TempFile(os.TempDir(), "TEST-gokrb5-krb5.conf")
defer os.Remove(cf.Name())
cf.WriteString(krb5Conf)
c, err := Load(cf.Name())
if err != nil {
t.Fatalf("Error loading config: %v", err)
}
assert.Equal(t, "TEST.GOKRB5", c.LibDefaults.DefaultRealm, "[libdefaults] default_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupRealm, "[libdefaults] dns_lookup_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupKDC, "[libdefaults] dns_lookup_kdc not as expected")
assert.Equal(t, time.Duration(10)*time.Hour, c.LibDefaults.TicketLifetime, "[libdefaults] Ticket lifetime not as expected")
assert.Equal(t, true, c.LibDefaults.Forwardable, "[libdefaults] forwardable not as expected")
assert.Equal(t, "FILE:/etc/krb5.keytab", c.LibDefaults.DefaultKeytabName, "[libdefaults] default_keytab_name not as expected")
assert.Equal(t, "FILE:/home/gokrb5/client.keytab", c.LibDefaults.DefaultClientKeytabName, "[libdefaults] default_client_keytab_name not as expected")
assert.Equal(t, []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96"}, c.LibDefaults.DefaultTktEnctypes, "[libdefaults] default_tkt_enctypes not as expected")
assert.Equal(t, 2, len(c.Realms), "Number of realms not as expected")
assert.Equal(t, "TEST.GOKRB5", c.Realms[0].Realm, "[realm] realm name not as expectd")
assert.Equal(t, []string{"10.80.88.88:749"}, c.Realms[0].AdminServer, "[realm] Admin_server not as expectd")
assert.Equal(t, []string{"10.80.88.88:464"}, c.Realms[0].KPasswdServer, "[realm] Kpasswd_server not as expectd")
assert.Equal(t, "test.gokrb5", c.Realms[0].DefaultDomain, "[realm] Default_domain not as expectd")
assert.Equal(t, []string{"10.80.88.88:88", "assume.port.num:88", "some.other.port:1234", "10.80.88.88:88"}, c.Realms[0].KDC, "[realm] Kdc not as expectd")
assert.Equal(t, []string{"kerberos.example.com:88", "kerberos-1.example.com:88"}, c.Realms[1].KDC, "[realm] Kdc not as expectd")
assert.Equal(t, []string{"kerberos.example.com"}, c.Realms[1].AdminServer, "[realm] Admin_server not as expectd")
assert.Equal(t, "TEST.GOKRB5", c.DomainRealm[".test.gokrb5"], "Domain to realm mapping not as expected")
assert.Equal(t, "TEST.GOKRB5", c.DomainRealm["test.gokrb5"], "Domain to realm mapping not as expected")
}
func TestLoad2(t *testing.T) {
t.Parallel()
c, err := NewConfigFromString(krb5Conf2)
if err != nil {
t.Fatalf("Error loading config: %v", err)
}
assert.Equal(t, "TEST.GOKRB5", c.LibDefaults.DefaultRealm, "[libdefaults] default_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupRealm, "[libdefaults] dns_lookup_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupKDC, "[libdefaults] dns_lookup_kdc not as expected")
assert.Equal(t, time.Duration(10)*time.Hour, c.LibDefaults.TicketLifetime, "[libdefaults] Ticket lifetime not as expected")
assert.Equal(t, true, c.LibDefaults.Forwardable, "[libdefaults] forwardable not as expected")
assert.Equal(t, "FILE:/etc/krb5.keytab", c.LibDefaults.DefaultKeytabName, "[libdefaults] default_keytab_name not as expected")
assert.Equal(t, "FILE:/home/gokrb5/client.keytab", c.LibDefaults.DefaultClientKeytabName, "[libdefaults] default_client_keytab_name not as expected")
assert.Equal(t, []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96"}, c.LibDefaults.DefaultTktEnctypes, "[libdefaults] default_tkt_enctypes not as expected")
assert.Equal(t, 2, len(c.Realms), "Number of realms not as expected")
assert.Equal(t, "TEST.GOKRB5", c.Realms[0].Realm, "[realm] realm name not as expectd")
assert.Equal(t, []string{"10.80.88.88:749"}, c.Realms[0].AdminServer, "[realm] Admin_server not as expectd")
assert.Equal(t, []string{"10.80.88.88:464"}, c.Realms[0].KPasswdServer, "[realm] Kpasswd_server not as expectd")
assert.Equal(t, "test.gokrb5", c.Realms[0].DefaultDomain, "[realm] Default_domain not as expectd")
assert.Equal(t, []string{"10.80.88.88:88", "assume.port.num:88", "some.other.port:1234", "10.80.88.88:88"}, c.Realms[0].KDC, "[realm] Kdc not as expectd")
assert.Equal(t, []string{"kerberos.example.com:88", "kerberos-1.example.com:88"}, c.Realms[1].KDC, "[realm] Kdc not as expectd")
assert.Equal(t, []string{"kerberos.example.com"}, c.Realms[1].AdminServer, "[realm] Admin_server not as expectd")
assert.Equal(t, "TEST.GOKRB5", c.DomainRealm[".test.gokrb5"], "Domain to realm mapping not as expected")
assert.Equal(t, "TEST.GOKRB5", c.DomainRealm["test.gokrb5"], "Domain to realm mapping not as expected")
assert.True(t, c.LibDefaults.NoAddresses, "No address not set as true")
}
func TestLoadNoBlankLines(t *testing.T) {
t.Parallel()
c, err := NewConfigFromString(krb5ConfNoBlankLines)
if err != nil {
t.Fatalf("Error loading config: %v", err)
}
assert.Equal(t, "TEST.GOKRB5", c.LibDefaults.DefaultRealm, "[libdefaults] default_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupRealm, "[libdefaults] dns_lookup_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupKDC, "[libdefaults] dns_lookup_kdc not as expected")
assert.Equal(t, time.Duration(10)*time.Hour, c.LibDefaults.TicketLifetime, "[libdefaults] Ticket lifetime not as expected")
assert.Equal(t, true, c.LibDefaults.Forwardable, "[libdefaults] forwardable not as expected")
assert.Equal(t, "FILE:/etc/krb5.keytab", c.LibDefaults.DefaultKeytabName, "[libdefaults] default_keytab_name not as expected")
assert.Equal(t, "FILE:/home/gokrb5/client.keytab", c.LibDefaults.DefaultClientKeytabName, "[libdefaults] default_client_keytab_name not as expected")
assert.Equal(t, []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96"}, c.LibDefaults.DefaultTktEnctypes, "[libdefaults] default_tkt_enctypes not as expected")
assert.Equal(t, 2, len(c.Realms), "Number of realms not as expected")
assert.Equal(t, "TEST.GOKRB5", c.Realms[0].Realm, "[realm] realm name not as expectd")
assert.Equal(t, []string{"10.80.88.88:749"}, c.Realms[0].AdminServer, "[realm] Admin_server not as expectd")
assert.Equal(t, []string{"10.80.88.88:464"}, c.Realms[0].KPasswdServer, "[realm] Kpasswd_server not as expectd")
assert.Equal(t, "test.gokrb5", c.Realms[0].DefaultDomain, "[realm] Default_domain not as expectd")
assert.Equal(t, []string{"10.80.88.88:88", "assume.port.num:88", "some.other.port:1234", "10.80.88.88:88"}, c.Realms[0].KDC, "[realm] Kdc not as expectd")
assert.Equal(t, []string{"kerberos.example.com:88", "kerberos-1.example.com:88"}, c.Realms[1].KDC, "[realm] Kdc not as expectd")
assert.Equal(t, []string{"kerberos.example.com"}, c.Realms[1].AdminServer, "[realm] Admin_server not as expectd")
assert.Equal(t, "TEST.GOKRB5", c.DomainRealm[".test.gokrb5"], "Domain to realm mapping not as expected")
assert.Equal(t, "TEST.GOKRB5", c.DomainRealm["test.gokrb5"], "Domain to realm mapping not as expected")
}
func TestLoadTabs(t *testing.T) {
t.Parallel()
cf, _ := ioutil.TempFile(os.TempDir(), "TEST-gokrb5-krb5.conf")
defer os.Remove(cf.Name())
cf.WriteString(krb5ConfTabs)
c, err := Load(cf.Name())
if err != nil {
t.Fatalf("Error loading config: %v", err)
}
assert.Equal(t, "TEST.GOKRB5", c.LibDefaults.DefaultRealm, "[libdefaults] default_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupRealm, "[libdefaults] dns_lookup_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupKDC, "[libdefaults] dns_lookup_kdc not as expected")
assert.Equal(t, time.Duration(10)*time.Hour, c.LibDefaults.TicketLifetime, "[libdefaults] Ticket lifetime not as expected")
assert.Equal(t, true, c.LibDefaults.Forwardable, "[libdefaults] forwardable not as expected")
assert.Equal(t, "FILE:/etc/krb5.keytab", c.LibDefaults.DefaultKeytabName, "[libdefaults] default_keytab_name not as expected")
assert.Equal(t, "FILE:/home/gokrb5/client.keytab", c.LibDefaults.DefaultClientKeytabName, "[libdefaults] default_client_keytab_name not as expected")
assert.Equal(t, []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96"}, c.LibDefaults.DefaultTktEnctypes, "[libdefaults] default_tkt_enctypes not as expected")
assert.Equal(t, 2, len(c.Realms), "Number of realms not as expected")
assert.Equal(t, "TEST.GOKRB5", c.Realms[0].Realm, "[realm] realm name not as expectd")
assert.Equal(t, []string{"10.80.88.88:749"}, c.Realms[0].AdminServer, "[realm] Admin_server not as expectd")
assert.Equal(t, []string{"10.80.88.88:464"}, c.Realms[0].KPasswdServer, "[realm] Kpasswd_server not as expectd")
assert.Equal(t, "test.gokrb5", c.Realms[0].DefaultDomain, "[realm] Default_domain not as expectd")
assert.Equal(t, []string{"10.80.88.88:88", "assume.port.num:88", "some.other.port:1234", "10.80.88.88:88"}, c.Realms[0].KDC, "[realm] Kdc not as expectd")
assert.Equal(t, []string{"kerberos.example.com:88", "kerberos-1.example.com:88"}, c.Realms[1].KDC, "[realm] Kdc not as expectd")
assert.Equal(t, []string{"kerberos.example.com"}, c.Realms[1].AdminServer, "[realm] Admin_server not as expectd")
assert.Equal(t, "TEST.GOKRB5", c.DomainRealm[".test.gokrb5"], "Domain to realm mapping not as expected")
assert.Equal(t, "TEST.GOKRB5", c.DomainRealm["test.gokrb5"], "Domain to realm mapping not as expected")
}
func TestParseDuration(t *testing.T) {
t.Parallel()
// https://web.mit.edu/kerberos/krb5-1.12/doc/basic/date_format.html#duration
hms, _ := time.ParseDuration("12h30m15s")
hm, _ := time.ParseDuration("12h30m")
h, _ := time.ParseDuration("12h")
var tests = []struct {
timeStr string
duration time.Duration
}{
{"100", time.Duration(100) * time.Second},
{"12:30", hm},
{"12:30:15", hms},
{"1d12h30m15s", time.Duration(24)*time.Hour + hms},
{"1d12h30m", time.Duration(24)*time.Hour + hm},
{"1d12h", time.Duration(24)*time.Hour + h},
{"1d", time.Duration(24) * time.Hour},
}
for _, test := range tests {
d, err := parseDuration(test.timeStr)
if err != nil {
t.Errorf("error parsing %s: %v", test.timeStr, err)
}
assert.Equal(t, test.duration, d, "Duration not as expected for: "+test.timeStr)
}
}
func TestResolveRealm(t *testing.T) {
t.Parallel()
c, err := NewConfigFromString(krb5Conf)
if err != nil {
t.Fatalf("Error loading config: %v", err)
}
tests := []struct {
domainName string
want string
}{
{"unknown.com", "TEST.GOKRB5"},
{"hostname1.example.com", "EXAMPLE.COM"},
{"hostname2.example.com", "TEST.GOKRB5"},
{"one.two.three.example.com", "EXAMPLE.COM"},
{".test.gokrb5", "TEST.GOKRB5"},
}
for _, tt := range tests {
t.Run(tt.domainName, func(t *testing.T) {
if got := c.ResolveRealm(tt.domainName); got != tt.want {
t.Errorf("config.ResolveRealm() = %v, want %v", got, tt.want)
}
})
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/ 0000775 0000000 0000000 00000000000 13625372257 0023052 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/ccache.go 0000664 0000000 0000000 00000021126 13625372257 0024611 0 ustar 00root root 0000000 0000000 package credentials
import (
"bytes"
"encoding/binary"
"errors"
"io/ioutil"
"strings"
"time"
"unsafe"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
const (
headerFieldTagKDCOffset = 1
)
// 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
}
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) {
k, err := ioutil.ReadFile(cpath)
if err != nil {
return CCache{}, err
}
c, err := ParseCCache(k)
c.Path = cpath
return c, err
}
// ParseCCache byte slice of credential cache data into CCache type.
func ParseCCache(b []byte) (c CCache, err error) {
p := 0
//The first byte of the file always has the value 5
if int8(b[p]) != 5 {
err = errors.New("Invalid credential cache data. First byte does not equal 5")
return
}
p++
//Get credential cache version
//The second byte contains the version number (1 to 4)
c.Version = uint8(b[p])
if c.Version < 1 || c.Version > 4 {
err = errors.New("Invalid credential cache data. Keytab version is not within 1 to 4")
if err != nil {
return
}
}
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
}
}
c.DefaultPrincipal = parsePrincipal(b, &p, &c, &endian)
for p < len(b) {
cred, e := parseCredential(b, &p, &c, &endian)
if e != nil {
err = e
return
}
c.Credentials = append(c.Credentials, cred)
}
return
}
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 = int32(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 < int(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.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.GetPrincipalNameString(),
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) {
var cred 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 {
var creds []credential
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 {
// 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
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/ccache_integration_test.go 0000664 0000000 0000000 00000007104 13625372257 0030253 0 ustar 00root root 0000000 0000000 // +build integration
// To turn on this test use -tags=integration in go test command
package credentials
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"os/exec"
"os/user"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
const (
kinitCmd = "kinit"
kvnoCmd = "kvno"
klistCmd = "klist"
spn = "HTTP/host.test.gokrb5"
)
type output struct {
buf *bytes.Buffer
lines []string
*sync.Mutex
}
func newOutput() *output {
return &output{
buf: &bytes.Buffer{},
lines: []string{},
Mutex: &sync.Mutex{},
}
}
func (rw *output) Write(p []byte) (int, error) {
rw.Lock()
defer rw.Unlock()
return rw.buf.Write(p)
}
func (rw *output) Lines() []string {
rw.Lock()
defer rw.Unlock()
s := bufio.NewScanner(rw.buf)
for s.Scan() {
rw.lines = append(rw.lines, s.Text())
}
return rw.lines
}
func login() error {
file, err := os.Create("/etc/krb5.conf")
if err != nil {
return fmt.Errorf("cannot open krb5.conf: %v", err)
}
defer file.Close()
fmt.Fprintf(file, testdata.TEST_KRB5CONF)
cmd := exec.Command(kinitCmd, "testuser1@TEST.GOKRB5")
stdinR, stdinW := io.Pipe()
stderrR, stderrW := io.Pipe()
cmd.Stdin = stdinR
cmd.Stderr = stderrW
err = cmd.Start()
if err != nil {
return fmt.Errorf("could not start %s command: %v", kinitCmd, err)
}
go func() {
io.WriteString(stdinW, "passwordvalue")
stdinW.Close()
}()
errBuf := new(bytes.Buffer)
go func() {
io.Copy(errBuf, stderrR)
stderrR.Close()
}()
err = cmd.Wait()
if err != nil {
return fmt.Errorf("%s did not run successfully: %v stderr: %s", kinitCmd, err, string(errBuf.Bytes()))
}
return nil
}
func getServiceTkt() error {
cmd := exec.Command(kvnoCmd, spn)
err := cmd.Start()
if err != nil {
return fmt.Errorf("could not start %s command: %v", kvnoCmd, err)
}
err = cmd.Wait()
if err != nil {
return fmt.Errorf("%s did not run successfully: %v", kvnoCmd, err)
}
return nil
}
func klist() ([]string, error) {
cmd := exec.Command(klistCmd, "-Aef")
stdout := newOutput()
cmd.Stdout = stdout
err := cmd.Start()
if err != nil {
return []string{}, fmt.Errorf("could not start %s command: %v", klistCmd, err)
}
err = cmd.Wait()
if err != nil {
return []string{}, fmt.Errorf("%s did not run successfully: %v", klistCmd, err)
}
return stdout.Lines(), nil
}
func loadCCache() (CCache, error) {
usr, _ := user.Current()
cpath := "/tmp/krb5cc_" + usr.Uid
return LoadCCache(cpath)
}
func TestLoadCCache(t *testing.T) {
err := login()
if err != nil {
t.Fatalf("error logging in with kinit: %v", err)
}
c, err := loadCCache()
if err != nil {
t.Errorf("error loading CCache: %v", err)
}
pn := c.GetClientPrincipalName()
assert.Equal(t, "testuser1", pn.GetPrincipalNameString(), "principal not as expected")
assert.Equal(t, "TEST.GOKRB5", c.GetClientRealm(), "realm not as expected")
}
func TestCCacheEntries(t *testing.T) {
err := login()
if err != nil {
t.Fatalf("error logging in with kinit: %v", err)
}
err = getServiceTkt()
if err != nil {
t.Fatalf("error getting service ticket: %v", err)
}
clist, _ := klist()
t.Log("OS Creds Cache contents:")
for _, l := range clist {
t.Log(l)
}
c, err := loadCCache()
if err != nil {
t.Errorf("error loading CCache: %v", err)
}
creds := c.GetEntries()
var found bool
n := types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, spn)
for _, cred := range creds {
if cred.Server.PrincipalName.Equal(n) {
found = true
break
}
}
if !found {
t.Errorf("Entry for %s not found in CCache", spn)
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/ccache_test.go 0000664 0000000 0000000 00000007554 13625372257 0025661 0 ustar 00root root 0000000 0000000 package credentials
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
func TestParse(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.CCACHE_TEST)
if err != nil {
t.Fatal("Error decoding test data")
}
c, err := ParseCCache(b)
if err != nil {
t.Fatalf("Error parsing cache: %v", err)
}
assert.Equal(t, uint8(4), c.Version, "Version not as expected")
assert.Equal(t, 1, len(c.Header.fields), "Number of header fields not as expected")
assert.Equal(t, uint16(1), c.Header.fields[0].tag, "Header tag not as expected")
assert.Equal(t, uint16(8), c.Header.fields[0].length, "Length of header not as expected")
assert.Equal(t, "TEST.GOKRB5", c.DefaultPrincipal.Realm, "Default client principal realm not as expected")
assert.Equal(t, "testuser1", c.DefaultPrincipal.PrincipalName.GetPrincipalNameString(), "Default client principaal name not as expected")
assert.Equal(t, 3, len(c.Credentials), "Number of credentials not as expected")
tgtpn := types.PrincipalName{
NameType: nametype.KRB_NT_SRV_INST,
NameString: []string{"krbtgt", "TEST.GOKRB5"},
}
assert.True(t, c.Contains(tgtpn), "Cache does not contain TGT credential")
httppn := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
assert.True(t, c.Contains(httppn), "Cache does not contain HTTP SPN credential")
}
func TestCCache_GetClientPrincipalName(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.CCACHE_TEST)
if err != nil {
t.Fatal("Error decoding test data")
}
c, err := ParseCCache(b)
if err != nil {
t.Fatalf("Error parsing cache: %v", err)
}
pn := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"testuser1"},
}
assert.Equal(t, pn, c.GetClientPrincipalName(), "Client PrincipalName not as expected")
}
func TestCCache_GetClientCredentials(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.CCACHE_TEST)
if err != nil {
t.Fatal("Error decoding test data")
}
c, err := ParseCCache(b)
if err != nil {
t.Fatalf("Error parsing cache: %v", err)
}
pn := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"testuser1"},
}
cred := c.GetClientCredentials()
assert.Equal(t, "TEST.GOKRB5", cred.Realm, "Client realm in credential not as expected")
assert.Equal(t, pn, cred.CName, "Client Principal Name not as expected")
assert.Equal(t, "testuser1", cred.Username, "Username not as expected")
}
func TestCCache_GetClientRealm(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.CCACHE_TEST)
if err != nil {
t.Fatal("Error decoding test data")
}
c, err := ParseCCache(b)
if err != nil {
t.Fatalf("Error parsing cache: %v", err)
}
assert.Equal(t, "TEST.GOKRB5", c.GetClientRealm(), "Client realm not as expected")
}
func TestCCache_GetEntry(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.CCACHE_TEST)
if err != nil {
t.Fatal("Error decoding test data")
}
c, err := ParseCCache(b)
if err != nil {
t.Fatalf("Error parsing cache: %v", err)
}
httppn := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
cred, ok := c.GetEntry(httppn)
if !ok {
t.Fatal("Could not get entry from CCache as not found")
}
assert.Equal(t, httppn, cred.Server.PrincipalName, "Credential does not have the right server principal name")
}
func TestCCache_GetEntries(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.CCACHE_TEST)
if err != nil {
t.Fatal("Error decoding test data")
}
c, err := ParseCCache(b)
if err != nil {
t.Fatalf("Error parsing cache: %v", err)
}
creds := c.GetEntries()
assert.Equal(t, 2, len(creds), "Number of credentials entries not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/credentials.go 0000664 0000000 0000000 00000015313 13625372257 0025701 0 ustar 00root root 0000000 0000000 // Package credentials provides credentials management for Kerberos 5 authentication.
package credentials
import (
"time"
"github.com/hashicorp/go-uuid"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
const (
// AttributeKeyADCredentials assigned number for AD credentials.
AttributeKeyADCredentials = 1
)
// 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[int]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
}
// NewCredentials creates a new Credentials instance.
func NewCredentials(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.NewKeytab(),
Attributes: make(map[int]interface{}),
sessionID: uid,
}
}
// NewCredentialsFromPrincipal creates a new Credentials instance with the user details provides as a PrincipalName type.
func NewCredentialsFromPrincipal(cname types.PrincipalName, realm string) Credentials {
uid, err := uuid.GenerateUUID()
if err != nil {
uid = "00unique-sess-ions-uuid-unavailable0"
}
return Credentials{
Username: cname.GetPrincipalNameString(),
displayName: cname.GetPrincipalNameString(),
Realm: realm,
CName: cname,
Keytab: keytab.NewKeytab(),
Attributes: make(map[int]interface{}),
groupMembership: make(map[string]bool),
sessionID: uid,
}
}
// WithKeytab sets the Keytab in the Credentials struct.
func (c *Credentials) WithKeytab(kt keytab.Keytab) *Credentials {
c.Keytab = kt
return c
}
// WithPassword sets the password in the Credentials struct.
func (c *Credentials) WithPassword(password string) *Credentials {
c.Password = password
return c
}
// HasKeytab queries if the Credentials has a keytab defined.
func (c *Credentials) HasKeytab() bool {
if len(c.Keytab.Entries) > 0 {
return true
}
return false
}
// SetValidUntil sets the expiry time of the credentials
func (c *Credentials) SetValidUntil(t time.Time) {
c.ValidUntil = t
}
// HasPassword queries if the Credentials has a password defined.
func (c *Credentials) HasPassword() bool {
if c.Password != "" {
return true
}
return false
}
// SetADCredentials adds ADCredentials attributes to the credentials
func (c *Credentials) SetADCredentials(a ADCredentials) {
c.Attributes[AttributeKeyADCredentials] = a
if a.FullName != "" {
c.SetDisplayName(a.FullName)
}
if a.EffectiveName != "" {
c.SetUserName(a.EffectiveName)
}
if a.LogonDomainName != "" {
c.SetDomain(a.LogonDomainName)
}
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
}
// 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
}
// 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
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/credentials_test.go 0000664 0000000 0000000 00000000516 13625372257 0026737 0 ustar 00root root 0000000 0000000 package credentials
import (
"github.com/stretchr/testify/assert"
goidentity "gopkg.in/jcmturner/goidentity.v2"
"testing"
)
func TestImplementsInterface(t *testing.T) {
t.Parallel()
u := new(Credentials)
i := new(goidentity.Identity)
assert.Implements(t, i, u, "Credentials type does not implement the Identity interface")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/ 0000775 0000000 0000000 00000000000 13625372257 0022075 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes128-cts-hmac-sha1-96.go 0000664 0000000 0000000 00000010527 13625372257 0026217 0 ustar 00root root 0000000 0000000 package crypto
import (
"crypto/aes"
"crypto/hmac"
"crypto/sha1"
"hash"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3961"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3962"
"gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
)
// RFC 3962
// Aes128CtsHmacSha96 implements Kerberos encryption type aes128-cts-hmac-sha1-96
type Aes128CtsHmacSha96 struct {
}
// GetETypeID returns the EType ID number.
func (e Aes128CtsHmacSha96) GetETypeID() int32 {
return etypeID.AES128_CTS_HMAC_SHA1_96
}
// GetHashID returns the checksum type ID number.
func (e Aes128CtsHmacSha96) GetHashID() int32 {
return chksumtype.HMAC_SHA1_96_AES128
}
// GetKeyByteSize returns the number of bytes for key of this etype.
func (e Aes128CtsHmacSha96) GetKeyByteSize() int {
return 128 / 8
}
// GetKeySeedBitLength returns the number of bits for the seed for key generation.
func (e Aes128CtsHmacSha96) GetKeySeedBitLength() int {
return e.GetKeyByteSize() * 8
}
// GetHashFunc returns the hash function for this etype.
func (e Aes128CtsHmacSha96) GetHashFunc() func() hash.Hash {
return sha1.New
}
// GetMessageBlockByteSize returns the block size for the etype's messages.
func (e Aes128CtsHmacSha96) GetMessageBlockByteSize() int {
return 1
}
// GetDefaultStringToKeyParams returns the default key derivation parameters in string form.
func (e Aes128CtsHmacSha96) GetDefaultStringToKeyParams() string {
return "00001000"
}
// GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations.
func (e Aes128CtsHmacSha96) GetConfounderByteSize() int {
return aes.BlockSize
}
// GetHMACBitLength returns the bit count size of the integrity hash.
func (e Aes128CtsHmacSha96) GetHMACBitLength() int {
return 96
}
// GetCypherBlockBitLength returns the bit count size of the cypher block.
func (e Aes128CtsHmacSha96) GetCypherBlockBitLength() int {
return aes.BlockSize * 8
}
// StringToKey returns a key derived from the string provided.
func (e Aes128CtsHmacSha96) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
return rfc3962.StringToKey(secret, salt, s2kparams, e)
}
// RandomToKey returns a key from the bytes provided.
func (e Aes128CtsHmacSha96) RandomToKey(b []byte) []byte {
return rfc3961.RandomToKey(b)
}
// EncryptData encrypts the data provided.
func (e Aes128CtsHmacSha96) EncryptData(key, data []byte) ([]byte, []byte, error) {
return rfc3962.EncryptData(key, data, e)
}
// EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message.
func (e Aes128CtsHmacSha96) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
return rfc3962.EncryptMessage(key, message, usage, e)
}
// DecryptData decrypts the data provided.
func (e Aes128CtsHmacSha96) DecryptData(key, data []byte) ([]byte, error) {
return rfc3962.DecryptData(key, data, e)
}
// DecryptMessage decrypts the message provided and verifies the integrity of the message.
func (e Aes128CtsHmacSha96) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
return rfc3962.DecryptMessage(key, ciphertext, usage, e)
}
// DeriveKey derives a key from the protocol key based on the usage value.
func (e Aes128CtsHmacSha96) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
return rfc3961.DeriveKey(protocolKey, usage, e)
}
// DeriveRandom generates data needed for key generation.
func (e Aes128CtsHmacSha96) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
return rfc3961.DeriveRandom(protocolKey, usage, e)
}
// VerifyIntegrity checks the integrity of the plaintext message.
func (e Aes128CtsHmacSha96) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {
return rfc3961.VerifyIntegrity(protocolKey, ct, pt, usage, e)
}
// GetChecksumHash returns a keyed checksum hash of the bytes provided.
func (e Aes128CtsHmacSha96) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) {
return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e)
}
// VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided.
func (e Aes128CtsHmacSha96) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool {
c, err := e.GetChecksumHash(protocolKey, data, usage)
if err != nil {
return false
}
return hmac.Equal(chksum, c)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes128-cts-hmac-sha1-96_test.go 0000664 0000000 0000000 00000003645 13625372257 0027261 0 ustar 00root root 0000000 0000000 package crypto
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3962"
)
func TestAes128CtsHmacSha196_StringToKey(t *testing.T) {
t.Parallel()
// Test vectors from RFC 3962 Appendix B
b, _ := hex.DecodeString("1234567878563412")
s := string(b)
b, _ = hex.DecodeString("f09d849e")
s2 := string(b)
var tests = []struct {
iterations int64
phrase string
salt string
pbkdf2 string
key string
}{
{1, "password", "ATHENA.MIT.EDUraeburn", "cdedb5281bb2f801565a1122b2563515", "42263c6e89f4fc28b8df68ee09799f15"},
{2, "password", "ATHENA.MIT.EDUraeburn", "01dbee7f4a9e243e988b62c73cda935d", "c651bf29e2300ac27fa469d693bdda13"},
{1200, "password", "ATHENA.MIT.EDUraeburn", "5c08eb61fdf71e4e4ec3cf6ba1f5512b", "4c01cd46d632d01e6dbe230a01ed642a"},
{5, "password", s, "d1daa78615f287e6a1c8b120d7062a49", "e9b23d52273747dd5c35cb55be619d8e"},
{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase equals block size", "139c30c0966bc32ba55fdbf212530ac9", "59d1bb789a828b1aa54ef9c2883f69ed"},
{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase exceeds block size", "9ccad6d468770cd51b10e6a68721be61", "cb8005dc5f90179a7f02104c0018751d"},
{50, s2, "EXAMPLE.COMpianist", "6b9cf26d45455a43a5b8bb276a403b39", "f149c1f2e154a73452d43e7fe62a56e5"},
}
var e Aes128CtsHmacSha96
for i, test := range tests {
assert.Equal(t, test.pbkdf2, hex.EncodeToString(rfc3962.StringToPBKDF2(test.phrase, test.salt, test.iterations, e)), "PBKDF2 not as expected")
k, err := e.StringToKey(test.phrase, test.salt, common.IterationsToS2Kparams(uint32(test.iterations)))
if err != nil {
t.Errorf("error in processing string to key for test %d: %v", i, err)
}
assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected")
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes128-cts-hmac-sha256-128.go 0000664 0000000 0000000 00000011165 13625372257 0026446 0 ustar 00root root 0000000 0000000 package crypto
import (
"crypto/aes"
"crypto/hmac"
"crypto/sha256"
"hash"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc8009"
"gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
)
// RFC https://tools.ietf.org/html/rfc8009
// Aes128CtsHmacSha256128 implements Kerberos encryption type aes128-cts-hmac-sha256-128
type Aes128CtsHmacSha256128 struct {
}
// GetETypeID returns the EType ID number.
func (e Aes128CtsHmacSha256128) GetETypeID() int32 {
return etypeID.AES128_CTS_HMAC_SHA256_128
}
// GetHashID returns the checksum type ID number.
func (e Aes128CtsHmacSha256128) GetHashID() int32 {
return chksumtype.HMAC_SHA256_128_AES128
}
// GetKeyByteSize returns the number of bytes for key of this etype.
func (e Aes128CtsHmacSha256128) GetKeyByteSize() int {
return 128 / 8
}
// GetKeySeedBitLength returns the number of bits for the seed for key generation.
func (e Aes128CtsHmacSha256128) GetKeySeedBitLength() int {
return e.GetKeyByteSize() * 8
}
// GetHashFunc returns the hash function for this etype.
func (e Aes128CtsHmacSha256128) GetHashFunc() func() hash.Hash {
return sha256.New
}
// GetMessageBlockByteSize returns the block size for the etype's messages.
func (e Aes128CtsHmacSha256128) GetMessageBlockByteSize() int {
return 1
}
// GetDefaultStringToKeyParams returns the default key derivation parameters in string form.
func (e Aes128CtsHmacSha256128) GetDefaultStringToKeyParams() string {
return "00008000"
}
// GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations.
func (e Aes128CtsHmacSha256128) GetConfounderByteSize() int {
return aes.BlockSize
}
// GetHMACBitLength returns the bit count size of the integrity hash.
func (e Aes128CtsHmacSha256128) GetHMACBitLength() int {
return 128
}
// GetCypherBlockBitLength returns the bit count size of the cypher block.
func (e Aes128CtsHmacSha256128) GetCypherBlockBitLength() int {
return aes.BlockSize * 8
}
// StringToKey returns a key derived from the string provided.
func (e Aes128CtsHmacSha256128) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
saltp := rfc8009.GetSaltP(salt, "aes128-cts-hmac-sha256-128")
return rfc8009.StringToKey(secret, saltp, s2kparams, e)
}
// RandomToKey returns a key from the bytes provided.
func (e Aes128CtsHmacSha256128) RandomToKey(b []byte) []byte {
return rfc8009.RandomToKey(b)
}
// EncryptData encrypts the data provided.
func (e Aes128CtsHmacSha256128) EncryptData(key, data []byte) ([]byte, []byte, error) {
return rfc8009.EncryptData(key, data, e)
}
// EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message.
func (e Aes128CtsHmacSha256128) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
return rfc8009.EncryptMessage(key, message, usage, e)
}
// DecryptData decrypts the data provided.
func (e Aes128CtsHmacSha256128) DecryptData(key, data []byte) ([]byte, error) {
return rfc8009.DecryptData(key, data, e)
}
// DecryptMessage decrypts the message provided and verifies the integrity of the message.
func (e Aes128CtsHmacSha256128) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
return rfc8009.DecryptMessage(key, ciphertext, usage, e)
}
// DeriveKey derives a key from the protocol key based on the usage value.
func (e Aes128CtsHmacSha256128) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
return rfc8009.DeriveKey(protocolKey, usage, e), nil
}
// DeriveRandom generates data needed for key generation.
func (e Aes128CtsHmacSha256128) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
return rfc8009.DeriveRandom(protocolKey, usage, e)
}
// VerifyIntegrity checks the integrity of the ciphertext message.
// Therefore the pt value to this interface method is not use. Pass any []byte.
func (e Aes128CtsHmacSha256128) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {
// We don't need ib just there for the interface
return rfc8009.VerifyIntegrity(protocolKey, ct, usage, e)
}
// GetChecksumHash returns a keyed checksum hash of the bytes provided.
func (e Aes128CtsHmacSha256128) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) {
return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e)
}
// VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided.
func (e Aes128CtsHmacSha256128) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool {
c, err := e.GetChecksumHash(protocolKey, data, usage)
if err != nil {
return false
}
return hmac.Equal(chksum, c)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes128-cts-hmac-sha256-128_test.go 0000664 0000000 0000000 00000014617 13625372257 0027512 0 ustar 00root root 0000000 0000000 package crypto
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc8009"
)
func TestAes128CtsHmacSha256128_StringToKey(t *testing.T) {
t.Parallel()
// Test vectors from RFC 8009 Appendix A
// Random 16bytes in test vector as string
r, _ := hex.DecodeString("10DF9DD783E5BC8ACEA1730E74355F61")
s := string(r)
var tests = []struct {
iterations uint32
phrase string
salt string
saltp string
key string
}{
{32768, "password", s + "ATHENA.MIT.EDUraeburn", "6165733132382d6374732d686d61632d7368613235362d3132380010df9dd783e5bc8acea1730e74355f61415448454e412e4d49542e4544557261656275726e", "089bca48b105ea6ea77ca5d2f39dc5e7"},
}
var e Aes128CtsHmacSha256128
for _, test := range tests {
saltp := rfc8009.GetSaltP(test.salt, "aes128-cts-hmac-sha256-128")
assert.Equal(t, test.saltp, hex.EncodeToString([]byte(saltp)), "SaltP not as expected")
k, _ := e.StringToKey(test.phrase, test.salt, common.IterationsToS2Kparams(test.iterations))
assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected")
}
}
func TestAes128CtsHmacSha256128_DeriveKey(t *testing.T) {
t.Parallel()
// Test vectors from RFC 8009 Appendix A
protocolBaseKey, _ := hex.DecodeString("3705d96080c17728a0e800eab6e0d23c")
testUsage := uint32(2)
var e Aes128CtsHmacSha256128
k, err := e.DeriveKey(protocolBaseKey, common.GetUsageKc(testUsage))
if err != nil {
t.Fatalf("Error deriving checksum key: %v", err)
}
assert.Equal(t, "b31a018a48f54776f403e9a396325dc3", hex.EncodeToString(k), "Checksum derived key not as epxected")
k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKe(testUsage))
if err != nil {
t.Fatalf("Error deriving encryption key: %v", err)
}
assert.Equal(t, "9b197dd1e8c5609d6e67c3e37c62c72e", hex.EncodeToString(k), "Encryption derived key not as epxected")
k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKi(testUsage))
if err != nil {
t.Fatalf("Error deriving integrity key: %v", err)
}
assert.Equal(t, "9fda0e56ab2d85e1569a688696c26a6c", hex.EncodeToString(k), "Integrity derived key not as epxected")
}
func TestAes128CtsHmacSha256128_VerifyIntegrity(t *testing.T) {
t.Parallel()
// Test vectors from RFC 8009
protocolBaseKey, _ := hex.DecodeString("3705d96080c17728a0e800eab6e0d23c")
testUsage := uint32(2)
var e Aes128CtsHmacSha256128
var tests = []struct {
kc string
pt string
chksum string
}{
{"b31a018a48f54776f403e9a396325dc3", "000102030405060708090a0b0c0d0e0f1011121314", "d78367186643d67b411cba9139fc1dee"},
}
for _, test := range tests {
p, _ := hex.DecodeString(test.pt)
b, err := e.GetChecksumHash(protocolBaseKey, p, testUsage)
if err != nil {
t.Errorf("error generating checksum: %v", err)
}
assert.Equal(t, test.chksum, hex.EncodeToString(b), "Checksum not as expected")
}
}
func TestAes128CtsHmacSha256128_Cypto(t *testing.T) {
t.Parallel()
protocolBaseKey, _ := hex.DecodeString("3705d96080c17728a0e800eab6e0d23c")
testUsage := uint32(2)
var tests = []struct {
plain string
confounder string
ke string
ki string
encrypted string // AESOutput
hash string // TruncatedHMACOutput
cipher string // Ciphertext(AESOutput|HMACOutput)
}{
// Test vectors from RFC 8009 Appendix A
{"", "7e5895eaf2672435bad817f545a37148", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "ef85fb890bb8472f4dab20394dca781d", "ad877eda39d50c870c0d5a0a8e48c718", "ef85fb890bb8472f4dab20394dca781dad877eda39d50c870c0d5a0a8e48c718"},
{"000102030405", "7bca285e2fd4130fb55b1a5c83bc5b24", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "84d7f30754ed987bab0bf3506beb09cfb55402cef7e6", "877ce99e247e52d16ed4421dfdf8976c", "84d7f30754ed987bab0bf3506beb09cfb55402cef7e6877ce99e247e52d16ed4421dfdf8976c"},
{"000102030405060708090a0b0c0d0e0f", "56ab21713ff62c0a1457200f6fa9948f", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "3517d640f50ddc8ad3628722b3569d2ae07493fa8263254080ea65c1008e8fc2", "95fb4852e7d83e1e7c48c37eebe6b0d3", "3517d640f50ddc8ad3628722b3569d2ae07493fa8263254080ea65c1008e8fc295fb4852e7d83e1e7c48c37eebe6b0d3"},
{"000102030405060708090a0b0c0d0e0f1011121314", "a7a4e29a4728ce10664fb64e49ad3fac", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "720f73b18d9859cd6ccb4346115cd336c70f58edc0c4437c5573544c31c813bce1e6d072c1", "86b39a413c2f92ca9b8334a287ffcbfc", "720f73b18d9859cd6ccb4346115cd336c70f58edc0c4437c5573544c31c813bce1e6d072c186b39a413c2f92ca9b8334a287ffcbfc"},
}
var e Aes128CtsHmacSha256128
for i, test := range tests {
m, _ := hex.DecodeString(test.plain)
b, _ := hex.DecodeString(test.encrypted)
ke, _ := hex.DecodeString(test.ke)
cf, _ := hex.DecodeString(test.confounder)
ct, _ := hex.DecodeString(test.cipher)
cfm := append(cf, m...)
// Test encryption to raw encrypted bytes
_, c, err := e.EncryptData(ke, cfm)
if err != nil {
t.Errorf("encryption failed for test %v: %v", i+1, err)
}
assert.Equal(t, test.encrypted, hex.EncodeToString(c), "Encrypted result not as expected - test %v", i)
// Test decryption of raw encrypted bytes
p, err := e.DecryptData(ke, b)
//Remove the confounder bytes
p = p[e.GetConfounderByteSize():]
if err != nil {
t.Errorf("decryption failed for test %v: %v", i+1, err)
}
assert.Equal(t, test.plain, hex.EncodeToString(p), "Decrypted result not as expected - test %v", i)
// Test integrity check of complete ciphertext message
assert.True(t, e.VerifyIntegrity(protocolBaseKey, ct, ct, testUsage), "Integrity check of cipher text failed")
// Test encrypting and decrypting a complete cipertext message (with confounder, integrity hash)
_, cm, err := e.EncryptMessage(protocolBaseKey, m, testUsage)
if err != nil {
t.Errorf("encryption to message failed for test %v: %v", i+1, err)
}
dm, err := e.DecryptMessage(protocolBaseKey, cm, testUsage)
if err != nil {
t.Errorf("decrypting complete encrypted message failed for test %v: %v", i+1, err)
}
assert.Equal(t, m, dm, "Message not as expected after encrypting and decrypting for test %v: %v", i+1, err)
// Test the integrity hash
ivz := make([]byte, e.GetConfounderByteSize())
hm := append(ivz, b...)
mac, _ := common.GetIntegrityHash(hm, protocolBaseKey, testUsage, e)
assert.Equal(t, test.hash, hex.EncodeToString(mac), "HMAC result not as expected - test %v", i)
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes256-cts-hmac-sha1-96.go 0000664 0000000 0000000 00000010527 13625372257 0026221 0 ustar 00root root 0000000 0000000 package crypto
import (
"crypto/aes"
"crypto/hmac"
"crypto/sha1"
"hash"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3961"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3962"
"gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
)
// RFC 3962
// Aes256CtsHmacSha96 implements Kerberos encryption type aes256-cts-hmac-sha1-96
type Aes256CtsHmacSha96 struct {
}
// GetETypeID returns the EType ID number.
func (e Aes256CtsHmacSha96) GetETypeID() int32 {
return etypeID.AES256_CTS_HMAC_SHA1_96
}
// GetHashID returns the checksum type ID number.
func (e Aes256CtsHmacSha96) GetHashID() int32 {
return chksumtype.HMAC_SHA1_96_AES256
}
// GetKeyByteSize returns the number of bytes for key of this etype.
func (e Aes256CtsHmacSha96) GetKeyByteSize() int {
return 256 / 8
}
// GetKeySeedBitLength returns the number of bits for the seed for key generation.
func (e Aes256CtsHmacSha96) GetKeySeedBitLength() int {
return e.GetKeyByteSize() * 8
}
// GetHashFunc returns the hash function for this etype.
func (e Aes256CtsHmacSha96) GetHashFunc() func() hash.Hash {
return sha1.New
}
// GetMessageBlockByteSize returns the block size for the etype's messages.
func (e Aes256CtsHmacSha96) GetMessageBlockByteSize() int {
return 1
}
// GetDefaultStringToKeyParams returns the default key derivation parameters in string form.
func (e Aes256CtsHmacSha96) GetDefaultStringToKeyParams() string {
return "00001000"
}
// GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations.
func (e Aes256CtsHmacSha96) GetConfounderByteSize() int {
return aes.BlockSize
}
// GetHMACBitLength returns the bit count size of the integrity hash.
func (e Aes256CtsHmacSha96) GetHMACBitLength() int {
return 96
}
// GetCypherBlockBitLength returns the bit count size of the cypher block.
func (e Aes256CtsHmacSha96) GetCypherBlockBitLength() int {
return aes.BlockSize * 8
}
// StringToKey returns a key derived from the string provided.
func (e Aes256CtsHmacSha96) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
return rfc3962.StringToKey(secret, salt, s2kparams, e)
}
// RandomToKey returns a key from the bytes provided.
func (e Aes256CtsHmacSha96) RandomToKey(b []byte) []byte {
return rfc3961.RandomToKey(b)
}
// EncryptData encrypts the data provided.
func (e Aes256CtsHmacSha96) EncryptData(key, data []byte) ([]byte, []byte, error) {
return rfc3962.EncryptData(key, data, e)
}
// EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message.
func (e Aes256CtsHmacSha96) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
return rfc3962.EncryptMessage(key, message, usage, e)
}
// DecryptData decrypts the data provided.
func (e Aes256CtsHmacSha96) DecryptData(key, data []byte) ([]byte, error) {
return rfc3962.DecryptData(key, data, e)
}
// DecryptMessage decrypts the message provided and verifies the integrity of the message.
func (e Aes256CtsHmacSha96) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
return rfc3962.DecryptMessage(key, ciphertext, usage, e)
}
// DeriveKey derives a key from the protocol key based on the usage value.
func (e Aes256CtsHmacSha96) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
return rfc3961.DeriveKey(protocolKey, usage, e)
}
// DeriveRandom generates data needed for key generation.
func (e Aes256CtsHmacSha96) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
return rfc3961.DeriveRandom(protocolKey, usage, e)
}
// VerifyIntegrity checks the integrity of the plaintext message.
func (e Aes256CtsHmacSha96) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {
return rfc3961.VerifyIntegrity(protocolKey, ct, pt, usage, e)
}
// GetChecksumHash returns a keyed checksum hash of the bytes provided.
func (e Aes256CtsHmacSha96) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) {
return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e)
}
// VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided.
func (e Aes256CtsHmacSha96) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool {
c, err := e.GetChecksumHash(protocolKey, data, usage)
if err != nil {
return false
}
return hmac.Equal(chksum, c)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes256-cts-hmac-sha1-96_test.go 0000664 0000000 0000000 00000004545 13625372257 0027263 0 ustar 00root root 0000000 0000000 package crypto
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3962"
)
func TestAes256CtsHmacSha196_StringToKey(t *testing.T) {
t.Parallel()
// Test vectors from RFC 3962 Appendix B
b, _ := hex.DecodeString("1234567878563412")
s := string(b)
b, _ = hex.DecodeString("f09d849e")
s2 := string(b)
var tests = []struct {
iterations int64
phrase string
salt string
pbkdf2 string
key string
}{
{1, "password", "ATHENA.MIT.EDUraeburn", "cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837", "fe697b52bc0d3ce14432ba036a92e65bbb52280990a2fa27883998d72af30161"},
{2, "password", "ATHENA.MIT.EDUraeburn", "01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86", "a2e16d16b36069c135d5e9d2e25f896102685618b95914b467c67622225824ff"},
{1200, "password", "ATHENA.MIT.EDUraeburn", "5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13", "55a6ac740ad17b4846941051e1e8b0a7548d93b0ab30a8bc3ff16280382b8c2a"},
{5, "password", s, "d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee", "97a4e786be20d81a382d5ebc96d5909cabcdadc87ca48f574504159f16c36e31"},
{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase equals block size", "139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1", "89adee3608db8bc71f1bfbfe459486b05618b70cbae22092534e56c553ba4b34"},
{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase exceeds block size", "9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a", "d78c5c9cb872a8c9dad4697f0bb5b2d21496c82beb2caeda2112fceea057401b"},
{50, s2, "EXAMPLE.COMpianist", "6b9cf26d45455a43a5b8bb276a403b39e7fe37a0c41e02c281ff3069e1e94f52", "4b6d9839f84406df1f09cc166db4b83c571848b784a3d6bdc346589a3e393f9e"},
}
var e Aes256CtsHmacSha96
for i, test := range tests {
assert.Equal(t, test.pbkdf2, hex.EncodeToString(rfc3962.StringToPBKDF2(test.phrase, test.salt, test.iterations, e)), "PBKDF2 not as expected")
k, err := e.StringToKey(test.phrase, test.salt, common.IterationsToS2Kparams(uint32(test.iterations)))
if err != nil {
t.Errorf("error in processing string to key for test %d: %v", i, err)
}
assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected")
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes256-cts-hmac-sha384-192.go 0000664 0000000 0000000 00000011170 13625372257 0026447 0 ustar 00root root 0000000 0000000 package crypto
import (
"crypto/aes"
"crypto/hmac"
"crypto/sha512"
"hash"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc8009"
"gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
)
// RFC https://tools.ietf.org/html/rfc8009
// Aes256CtsHmacSha384192 implements Kerberos encryption type aes256-cts-hmac-sha384-192
type Aes256CtsHmacSha384192 struct {
}
// GetETypeID returns the EType ID number.
func (e Aes256CtsHmacSha384192) GetETypeID() int32 {
return etypeID.AES256_CTS_HMAC_SHA384_192
}
// GetHashID returns the checksum type ID number.
func (e Aes256CtsHmacSha384192) GetHashID() int32 {
return chksumtype.HMAC_SHA384_192_AES256
}
// GetKeyByteSize returns the number of bytes for key of this etype.
func (e Aes256CtsHmacSha384192) GetKeyByteSize() int {
return 192 / 8
}
// GetKeySeedBitLength returns the number of bits for the seed for key generation.
func (e Aes256CtsHmacSha384192) GetKeySeedBitLength() int {
return e.GetKeyByteSize() * 8
}
// GetHashFunc returns the hash function for this etype.
func (e Aes256CtsHmacSha384192) GetHashFunc() func() hash.Hash {
return sha512.New384
}
// GetMessageBlockByteSize returns the block size for the etype's messages.
func (e Aes256CtsHmacSha384192) GetMessageBlockByteSize() int {
return 1
}
// GetDefaultStringToKeyParams returns the default key derivation parameters in string form.
func (e Aes256CtsHmacSha384192) GetDefaultStringToKeyParams() string {
return "00008000"
}
// GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations.
func (e Aes256CtsHmacSha384192) GetConfounderByteSize() int {
return aes.BlockSize
}
// GetHMACBitLength returns the bit count size of the integrity hash.
func (e Aes256CtsHmacSha384192) GetHMACBitLength() int {
return 192
}
// GetCypherBlockBitLength returns the bit count size of the cypher block.
func (e Aes256CtsHmacSha384192) GetCypherBlockBitLength() int {
return aes.BlockSize * 8
}
// StringToKey returns a key derived from the string provided.
func (e Aes256CtsHmacSha384192) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
saltp := rfc8009.GetSaltP(salt, "aes256-cts-hmac-sha384-192")
return rfc8009.StringToKey(secret, saltp, s2kparams, e)
}
// RandomToKey returns a key from the bytes provided.
func (e Aes256CtsHmacSha384192) RandomToKey(b []byte) []byte {
return rfc8009.RandomToKey(b)
}
// EncryptData encrypts the data provided.
func (e Aes256CtsHmacSha384192) EncryptData(key, data []byte) ([]byte, []byte, error) {
return rfc8009.EncryptData(key, data, e)
}
// EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message.
func (e Aes256CtsHmacSha384192) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
return rfc8009.EncryptMessage(key, message, usage, e)
}
// DecryptData decrypts the data provided.
func (e Aes256CtsHmacSha384192) DecryptData(key, data []byte) ([]byte, error) {
return rfc8009.DecryptData(key, data, e)
}
// DecryptMessage decrypts the message provided and verifies the integrity of the message.
func (e Aes256CtsHmacSha384192) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
return rfc8009.DecryptMessage(key, ciphertext, usage, e)
}
// DeriveKey derives a key from the protocol key based on the usage value.
func (e Aes256CtsHmacSha384192) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
return rfc8009.DeriveKey(protocolKey, usage, e), nil
}
// DeriveRandom generates data needed for key generation.
func (e Aes256CtsHmacSha384192) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
return rfc8009.DeriveRandom(protocolKey, usage, e)
}
// VerifyIntegrity checks the integrity of the ciphertext message.
// Therefore the pt value to this interface method is not use. Pass any []byte.
func (e Aes256CtsHmacSha384192) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {
// We don't need ib just there for the interface
return rfc8009.VerifyIntegrity(protocolKey, ct, usage, e)
}
// GetChecksumHash returns a keyed checksum hash of the bytes provided.
func (e Aes256CtsHmacSha384192) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) {
return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e)
}
// VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided.
func (e Aes256CtsHmacSha384192) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool {
c, err := e.GetChecksumHash(protocolKey, data, usage)
if err != nil {
return false
}
return hmac.Equal(chksum, c)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes256-cts-hmac-sha384-192_test.go 0000664 0000000 0000000 00000015656 13625372257 0027523 0 ustar 00root root 0000000 0000000 package crypto
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc8009"
)
func TestAes256CtsHmacSha384192_StringToKey(t *testing.T) {
t.Parallel()
// Test vectors from RFC 8009 Appendix A
// Random 16bytes in test vector as string
r, _ := hex.DecodeString("10DF9DD783E5BC8ACEA1730E74355F61")
s := string(r)
var tests = []struct {
iterations uint32
phrase string
salt string
saltp string
key string
}{
{32768, "password", s + "ATHENA.MIT.EDUraeburn", "6165733235362d6374732d686d61632d7368613338342d3139320010df9dd783e5bc8acea1730e74355f61415448454e412e4d49542e4544557261656275726e", "45bd806dbf6a833a9cffc1c94589a222367a79bc21c413718906e9f578a78467"},
}
var e Aes256CtsHmacSha384192
for _, test := range tests {
saltp := rfc8009.GetSaltP(test.salt, "aes256-cts-hmac-sha384-192")
assert.Equal(t, test.saltp, hex.EncodeToString([]byte(saltp)), "SaltP not as expected")
k, _ := e.StringToKey(test.phrase, test.salt, common.IterationsToS2Kparams(test.iterations))
assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected")
}
}
func TestAes256CtsHmacSha384192_DeriveKey(t *testing.T) {
t.Parallel()
// Test vectors from RFC 8009 Appendix A
protocolBaseKey, _ := hex.DecodeString("6d404d37faf79f9df0d33568d320669800eb4836472ea8a026d16b7182460c52")
testUsage := uint32(2)
var e Aes256CtsHmacSha384192
k, err := e.DeriveKey(protocolBaseKey, common.GetUsageKc(testUsage))
if err != nil {
t.Fatalf("Error deriving checksum key: %v", err)
}
assert.Equal(t, "ef5718be86cc84963d8bbb5031e9f5c4ba41f28faf69e73d", hex.EncodeToString(k), "Checksum derived key not as epxected")
k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKe(testUsage))
if err != nil {
t.Fatalf("Error deriving encryption key: %v", err)
}
assert.Equal(t, "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", hex.EncodeToString(k), "Encryption derived key not as epxected")
k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKi(testUsage))
if err != nil {
t.Fatalf("Error deriving integrity key: %v", err)
}
assert.Equal(t, "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", hex.EncodeToString(k), "Integrity derived key not as epxected")
}
func TestAes256CtsHmacSha384192_Cypto(t *testing.T) {
t.Parallel()
protocolBaseKey, _ := hex.DecodeString("6d404d37faf79f9df0d33568d320669800eb4836472ea8a026d16b7182460c52")
testUsage := uint32(2)
var tests = []struct {
plain string
confounder string
ke string
ki string
encrypted string // AESOutput
hash string // TruncatedHMACOutput
cipher string // Ciphertext(AESOutput|HMACOutput)
}{
// Test vectors from RFC 8009 Appendix A
{"", "f764e9fa15c276478b2c7d0c4e5f58e4", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "41f53fa5bfe7026d91faf9be959195a0", "58707273a96a40f0a01960621ac612748b9bbfbe7eb4ce3c", "41f53fa5bfe7026d91faf9be959195a058707273a96a40f0a01960621ac612748b9bbfbe7eb4ce3c"},
{"000102030405", "b80d3251c1f6471494256ffe712d0b9a", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "4ed7b37c2bcac8f74f23c1cf07e62bc7b75fb3f637b9", "f559c7f664f69eab7b6092237526ea0d1f61cb20d69d10f2", "4ed7b37c2bcac8f74f23c1cf07e62bc7b75fb3f637b9f559c7f664f69eab7b6092237526ea0d1f61cb20d69d10f2"},
{"000102030405060708090a0b0c0d0e0f", "53bf8a0d105265d4e276428624ce5e63", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "bc47ffec7998eb91e8115cf8d19dac4bbbe2e163e87dd37f49beca92027764f6", "8cf51f14d798c2273f35df574d1f932e40c4ff255b36a266", "bc47ffec7998eb91e8115cf8d19dac4bbbe2e163e87dd37f49beca92027764f68cf51f14d798c2273f35df574d1f932e40c4ff255b36a266"},
{"000102030405060708090a0b0c0d0e0f1011121314", "763e65367e864f02f55153c7e3b58af1", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "40013e2df58e8751957d2878bcd2d6fe101ccfd556cb1eae79db3c3ee86429f2b2a602ac86", "fef6ecb647d6295fae077a1feb517508d2c16b4192e01f62", "40013e2df58e8751957d2878bcd2d6fe101ccfd556cb1eae79db3c3ee86429f2b2a602ac86fef6ecb647d6295fae077a1feb517508d2c16b4192e01f62"},
}
var e Aes256CtsHmacSha384192
for i, test := range tests {
m, _ := hex.DecodeString(test.plain)
b, _ := hex.DecodeString(test.encrypted)
ke, _ := hex.DecodeString(test.ke)
cf, _ := hex.DecodeString(test.confounder)
ct, _ := hex.DecodeString(test.cipher)
cfm := append(cf, m...)
// Test encryption to raw encrypted bytes
_, c, err := e.EncryptData(ke, cfm)
if err != nil {
t.Errorf("encryption failed for test %v: %v", i+1, err)
}
assert.Equal(t, test.encrypted, hex.EncodeToString(c), "Encrypted result not as expected - test %v", i)
// Test decryption of raw encrypted bytes
p, err := e.DecryptData(ke, b)
//Remove the confounder bytes
p = p[e.GetConfounderByteSize():]
if err != nil {
t.Errorf("decryption failed for test %v: %v", i+1, err)
}
assert.Equal(t, test.plain, hex.EncodeToString(p), "Decrypted result not as expected - test %v", i)
// Test integrity check of complete ciphertext message
assert.True(t, e.VerifyIntegrity(protocolBaseKey, ct, ct, testUsage), "Integrity check of cipher text failed")
// Test encrypting and decrypting a complete cipertext message (with confounder, integrity hash)
_, cm, err := e.EncryptMessage(protocolBaseKey, m, testUsage)
if err != nil {
t.Errorf("encryption to message failed for test %v: %v", i+1, err)
}
dm, err := e.DecryptMessage(protocolBaseKey, cm, testUsage)
if err != nil {
t.Errorf("decrypting complete encrypted message failed for test %v: %v", i+1, err)
}
assert.Equal(t, m, dm, "Message not as expected after encrypting and decrypting for test %v: %v", i+1, err)
// Test the integrity hash
ivz := make([]byte, e.GetConfounderByteSize())
hm := append(ivz, b...)
mac, _ := common.GetIntegrityHash(hm, protocolBaseKey, testUsage, e)
assert.Equal(t, test.hash, hex.EncodeToString(mac), "HMAC result not as expected - test %v", i)
}
}
func TestAes256CtsHmacSha384192_VerifyIntegrity(t *testing.T) {
t.Parallel()
// Test vectors from RFC 8009
protocolBaseKey, _ := hex.DecodeString("6d404d37faf79f9df0d33568d320669800eb4836472ea8a026d16b7182460c52")
testUsage := uint32(2)
var e Aes256CtsHmacSha384192
var tests = []struct {
kc string
pt string
chksum string
}{
{"ef5718be86cc84963d8bbb5031e9f5c4ba41f28faf69e73d", "000102030405060708090a0b0c0d0e0f1011121314", "45ee791567eefca37f4ac1e0222de80d43c3bfa06699672a"},
}
for _, test := range tests {
p, _ := hex.DecodeString(test.pt)
b, err := e.GetChecksumHash(protocolBaseKey, p, testUsage)
if err != nil {
t.Errorf("error generating checksum: %v", err)
}
assert.Equal(t, test.chksum, hex.EncodeToString(b), "Checksum not as expected")
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/common/ 0000775 0000000 0000000 00000000000 13625372257 0023365 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/common/common.go 0000664 0000000 0000000 00000007232 13625372257 0025210 0 ustar 00root root 0000000 0000000 // Package common provides encryption methods common across encryption types
package common
import (
"bytes"
"crypto/hmac"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/crypto/etype"
)
// 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
}
// PKCS7Pad pads bytes according to RFC 2315 to nearest multiple of message size m.
func PKCS7Pad(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")
}
n := m - (len(b) % m)
pb := make([]byte, len(b)+n)
copy(pb, b)
copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n))
return pb, nil
}
// PKCS7Unpad removes RFC 2315 padding from byes where message size is m.
func PKCS7Unpad(b []byte, m int) ([]byte, error) {
if m <= 0 {
return nil, errors.New("invalid message block size when unpadding")
}
if b == nil || len(b) == 0 {
return nil, errors.New("padded data not valid: Zero size")
}
if len(b)%m != 0 {
return nil, errors.New("padded data not valid: Not multiple of message block size")
}
c := b[len(b)-1]
n := int(c)
if n == 0 || n > len(b) {
return nil, errors.New("padded data not valid: Data may not have been padded")
}
for i := 0; i < n; i++ {
if b[len(b)-n+i] != c {
return nil, errors.New("padded data not valid")
}
}
return b[:len(b)-n], nil
}
// GetHash generates the keyed hash value according to the etype's hash function.
func GetHash(pt, key []byte, usage []byte, etype etype.EType) ([]byte, error) {
k, err := etype.DeriveKey(key, usage)
if err != nil {
return nil, fmt.Errorf("unable to derive key for checksum: %v", err)
}
mac := hmac.New(etype.GetHashFunc(), k)
p := make([]byte, len(pt))
copy(p, pt)
mac.Write(p)
return mac.Sum(nil)[:etype.GetHMACBitLength()/8], nil
}
// GetChecksumHash returns a keyed checksum hash of the bytes provided.
func GetChecksumHash(b, key []byte, usage uint32, etype etype.EType) ([]byte, error) {
return GetHash(b, key, GetUsageKc(usage), etype)
}
// GetIntegrityHash returns a keyed integrity hash of the bytes provided.
func GetIntegrityHash(b, key []byte, usage uint32, etype etype.EType) ([]byte, error) {
return GetHash(b, key, GetUsageKi(usage), etype)
}
// VerifyChecksum compares the checksum of the msg bytes is the same as the checksum provided.
func VerifyChecksum(key, chksum, msg []byte, usage uint32, etype etype.EType) bool {
expectedMAC, _ := GetChecksumHash(msg, key, usage, etype)
return hmac.Equal(chksum, expectedMAC)
}
// GetUsageKc returns the checksum key usage value for the usage number un.
func GetUsageKc(un uint32) []byte {
return getUsage(un, 0x99)
}
// GetUsageKe returns the encryption key usage value for the usage number un
func GetUsageKe(un uint32) []byte {
return getUsage(un, 0xAA)
}
// GetUsageKi returns the integrity key usage value for the usage number un
func GetUsageKi(un uint32) []byte {
return getUsage(un, 0x55)
}
func getUsage(un uint32, o byte) []byte {
var buf bytes.Buffer
binary.Write(&buf, binary.BigEndian, un)
return append(buf.Bytes(), o)
}
// IterationsToS2Kparams converts the number of iterations as an integer to a string representation.
func IterationsToS2Kparams(i uint32) string {
b := make([]byte, 4, 4)
binary.BigEndian.PutUint32(b, i)
return hex.EncodeToString(b)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/crypto.go 0000664 0000000 0000000 00000011711 13625372257 0023745 0 ustar 00root root 0000000 0000000 // Package crypto implements cryptographic functions for Kerberos 5 implementation.
package crypto
import (
"encoding/hex"
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/crypto/etype"
"gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
"gopkg.in/jcmturner/gokrb5.v5/iana/patype"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// GetEtype returns an instances of the required etype struct for the etype ID.
func GetEtype(id int32) (etype.EType, error) {
switch id {
case etypeID.AES128_CTS_HMAC_SHA1_96:
var et Aes128CtsHmacSha96
return et, nil
case etypeID.AES256_CTS_HMAC_SHA1_96:
var et Aes256CtsHmacSha96
return et, nil
case etypeID.AES128_CTS_HMAC_SHA256_128:
var et Aes128CtsHmacSha256128
return et, nil
case etypeID.AES256_CTS_HMAC_SHA384_192:
var et Aes256CtsHmacSha384192
return et, nil
case etypeID.DES3_CBC_SHA1_KD:
var et Des3CbcSha1Kd
return et, nil
case etypeID.RC4_HMAC:
var et RC4HMAC
return et, nil
default:
return nil, fmt.Errorf("unknown or unsupported EType: %d", id)
}
}
// GetChksumEtype returns an instances of the required etype struct for the checksum ID.
func GetChksumEtype(id int32) (etype.EType, error) {
switch id {
case chksumtype.HMAC_SHA1_96_AES128:
var et Aes128CtsHmacSha96
return et, nil
case chksumtype.HMAC_SHA1_96_AES256:
var et Aes256CtsHmacSha96
return et, nil
case chksumtype.HMAC_SHA256_128_AES128:
var et Aes128CtsHmacSha256128
return et, nil
case chksumtype.HMAC_SHA384_192_AES256:
var et Aes256CtsHmacSha384192
return et, nil
case chksumtype.HMAC_SHA1_DES3_KD:
var et Des3CbcSha1Kd
return et, nil
case chksumtype.KERB_CHECKSUM_HMAC_MD5:
var et RC4HMAC
return et, nil
//case chksumtype.KERB_CHECKSUM_HMAC_MD5_UNSIGNED:
// var et RC4HMAC
// return et, nil
default:
return nil, fmt.Errorf("unknown or unsupported checksum type: %d", id)
}
}
// GetKeyFromPassword generates an encryption key from the principal's password.
func GetKeyFromPassword(passwd string, cname types.PrincipalName, realm string, etypeID int32, pas types.PADataSequence) (types.EncryptionKey, etype.EType, error) {
var key types.EncryptionKey
et, err := GetEtype(etypeID)
if err != nil {
return key, et, fmt.Errorf("error getting encryption type: %v", err)
}
sk2p := et.GetDefaultStringToKeyParams()
var salt string
var paID int32
for _, pa := range pas {
switch pa.PADataType {
case patype.PA_PW_SALT:
if paID > pa.PADataType {
continue
}
salt = string(pa.PADataValue)
case patype.PA_ETYPE_INFO:
if paID > pa.PADataType {
continue
}
var eti types.ETypeInfo
err := eti.Unmarshal(pa.PADataValue)
if err != nil {
return key, et, fmt.Errorf("error unmashaling PA Data to PA-ETYPE-INFO2: %v", err)
}
if etypeID != eti[0].EType {
et, err = GetEtype(eti[0].EType)
if err != nil {
return key, et, fmt.Errorf("error getting encryption type: %v", err)
}
}
salt = string(eti[0].Salt)
case patype.PA_ETYPE_INFO2:
if paID > pa.PADataType {
continue
}
var et2 types.ETypeInfo2
err := et2.Unmarshal(pa.PADataValue)
if err != nil {
return key, et, fmt.Errorf("error unmashalling PA Data to PA-ETYPE-INFO2: %v", err)
}
if etypeID != et2[0].EType {
et, err = GetEtype(et2[0].EType)
if err != nil {
return key, et, fmt.Errorf("error getting encryption type: %v", err)
}
}
if len(et2[0].S2KParams) == 4 {
sk2p = hex.EncodeToString(et2[0].S2KParams)
}
salt = et2[0].Salt
}
}
if salt == "" {
salt = cname.GetSalt(realm)
}
k, err := et.StringToKey(passwd, salt, sk2p)
if err != nil {
return key, et, fmt.Errorf("error deriving key from string: %+v", err)
}
key = types.EncryptionKey{
KeyType: etypeID,
KeyValue: k,
}
return key, et, nil
}
// GetEncryptedData encrypts the data provided and returns and EncryptedData type.
// Pass a usage value of zero to use the key provided directly rather than deriving one.
func GetEncryptedData(plainBytes []byte, key types.EncryptionKey, usage uint32, kvno int) (types.EncryptedData, error) {
var ed types.EncryptedData
et, err := GetEtype(key.KeyType)
if err != nil {
return ed, fmt.Errorf("error getting etype: %v", err)
}
_, b, err := et.EncryptMessage(key.KeyValue, plainBytes, usage)
if err != nil {
return ed, err
}
ed = types.EncryptedData{
EType: key.KeyType,
Cipher: b,
KVNO: kvno,
}
return ed, nil
}
// DecryptEncPart decrypts the EncryptedData.
func DecryptEncPart(ed types.EncryptedData, key types.EncryptionKey, usage uint32) ([]byte, error) {
return DecryptMessage(ed.Cipher, key, usage)
}
// DecryptMessage decrypts the ciphertext and verifies the integrity.
func DecryptMessage(ciphertext []byte, key types.EncryptionKey, usage uint32) ([]byte, error) {
et, err := GetEtype(key.KeyType)
if err != nil {
return []byte{}, fmt.Errorf("error decrypting: %v", err)
}
b, err := et.DecryptMessage(key.KeyValue, ciphertext, usage)
if err != nil {
return nil, fmt.Errorf("error decrypting: %v", err)
}
return b, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/des3-cbc-sha1-kd.go 0000664 0000000 0000000 00000010622 13625372257 0025236 0 ustar 00root root 0000000 0000000 package crypto
import (
"crypto/des"
"crypto/hmac"
"crypto/sha1"
"errors"
"hash"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3961"
"gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
)
//RFC: 3961 Section 6.3
// Des3CbcSha1Kd implements Kerberos encryption type des3-cbc-hmac-sha1-kd
type Des3CbcSha1Kd struct {
}
// GetETypeID returns the EType ID number.
func (e Des3CbcSha1Kd) GetETypeID() int32 {
return etypeID.DES3_CBC_SHA1_KD
}
// GetHashID returns the checksum type ID number.
func (e Des3CbcSha1Kd) GetHashID() int32 {
return chksumtype.HMAC_SHA1_DES3_KD
}
// GetKeyByteSize returns the number of bytes for key of this etype.
func (e Des3CbcSha1Kd) GetKeyByteSize() int {
return 24
}
// GetKeySeedBitLength returns the number of bits for the seed for key generation.
func (e Des3CbcSha1Kd) GetKeySeedBitLength() int {
return 21 * 8
}
// GetHashFunc returns the hash function for this etype.
func (e Des3CbcSha1Kd) GetHashFunc() func() hash.Hash {
return sha1.New
}
// GetMessageBlockByteSize returns the block size for the etype's messages.
func (e Des3CbcSha1Kd) GetMessageBlockByteSize() int {
return des.BlockSize
}
// GetDefaultStringToKeyParams returns the default key derivation parameters in string form.
func (e Des3CbcSha1Kd) GetDefaultStringToKeyParams() string {
var s string
return s
}
// GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations.
func (e Des3CbcSha1Kd) GetConfounderByteSize() int {
return des.BlockSize
}
// GetHMACBitLength returns the bit count size of the integrity hash.
func (e Des3CbcSha1Kd) GetHMACBitLength() int {
return e.GetHashFunc()().Size() * 8
}
// GetCypherBlockBitLength returns the bit count size of the cypher block.
func (e Des3CbcSha1Kd) GetCypherBlockBitLength() int {
return des.BlockSize * 8
}
// StringToKey returns a key derived from the string provided.
func (e Des3CbcSha1Kd) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
if s2kparams != "" {
return []byte{}, errors.New("s2kparams must be an empty string")
}
return rfc3961.DES3StringToKey(secret, salt, e)
}
// RandomToKey returns a key from the bytes provided.
func (e Des3CbcSha1Kd) RandomToKey(b []byte) []byte {
return rfc3961.DES3RandomToKey(b)
}
// DeriveRandom generates data needed for key generation.
func (e Des3CbcSha1Kd) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
r, err := rfc3961.DeriveRandom(protocolKey, usage, e)
return r, err
}
// DeriveKey derives a key from the protocol key based on the usage value.
func (e Des3CbcSha1Kd) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
r, err := e.DeriveRandom(protocolKey, usage)
if err != nil {
return nil, err
}
return e.RandomToKey(r), nil
}
// EncryptData encrypts the data provided.
func (e Des3CbcSha1Kd) EncryptData(key, data []byte) ([]byte, []byte, error) {
return rfc3961.DES3EncryptData(key, data, e)
}
// EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message.
func (e Des3CbcSha1Kd) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
return rfc3961.DES3EncryptMessage(key, message, usage, e)
}
// DecryptData decrypts the data provided.
func (e Des3CbcSha1Kd) DecryptData(key, data []byte) ([]byte, error) {
return rfc3961.DES3DecryptData(key, data, e)
}
// DecryptMessage decrypts the message provided and verifies the integrity of the message.
func (e Des3CbcSha1Kd) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
return rfc3961.DES3DecryptMessage(key, ciphertext, usage, e)
}
// VerifyIntegrity checks the integrity of the plaintext message.
func (e Des3CbcSha1Kd) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {
return rfc3961.VerifyIntegrity(protocolKey, ct, pt, usage, e)
}
// GetChecksumHash returns a keyed checksum hash of the bytes provided.
func (e Des3CbcSha1Kd) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) {
return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e)
}
// VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided.
func (e Des3CbcSha1Kd) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool {
c, err := e.GetChecksumHash(protocolKey, data, usage)
if err != nil {
return false
}
return hmac.Equal(chksum, c)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/des3-cbc-sha1-kd_test.go 0000664 0000000 0000000 00000006262 13625372257 0026302 0 ustar 00root root 0000000 0000000 package crypto
import (
"encoding/hex"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestDes3CbcSha1Kd_DR_DK(t *testing.T) {
t.Parallel()
// Test vectors from RFC 3961 Appendix A3
var tests = []struct {
key string
usage string
dr string
dk string
}{
{"dce06b1f64c857a11c3db57c51899b2cc1791008ce973b92", "0000000155", "935079d14490a75c3093c4a6e8c3b049c71e6ee705", "925179d04591a79b5d3192c4a7e9c289b049c71f6ee604cd"},
{"5e13d31c70ef765746578531cb51c15bf11ca82c97cee9f2", "00000001aa", "9f58e5a047d894101c469845d67ae3c5249ed812f2", "9e58e5a146d9942a101c469845d67a20e3c4259ed913f207"},
{"98e6fd8a04a4b6859b75a176540b9752bad3ecd610a252bc", "0000000155", "12fff90c773f956d13fc2ca0d0840349dbd39908eb", "13fef80d763e94ec6d13fd2ca1d085070249dad39808eabf"},
{"622aec25a2fe2cad7094680b7c64940280084c1a7cec92b5", "00000001aa", "f8debf05b097e7dc0603686aca35d91fd9a5516a70", "f8dfbf04b097e6d9dc0702686bcb3489d91fd9a4516b703e"},
{"d3f8298ccb166438dcb9b93ee5a7629286a491f838f802fb", "6b65726265726f73", "2270db565d2a3d64cfbfdc5305d4f778a6de42d9da", "2370da575d2a3da864cebfdc5204d56df779a7df43d9da43"},
{"c1081649ada74362e6a1459d01dfd30d67c2234c940704da", "0000000155", "348056ec98fcc517171d2b4d7a9493af482d999175", "348057ec98fdc48016161c2a4c7a943e92ae492c989175f7"},
{"5d154af238f46713155719d55e2f1f790dd661f279a7917c", "00000001aa", "a8818bc367dadacbe9a6c84627fb60c294b01215e5", "a8808ac267dada3dcbe9a7c84626fbc761c294b01315e5c1"},
{"798562e049852f57dc8c343ba17f2ca1d97394efc8adc443", "0000000155", "c813f88b3be2b2f75424ce9175fbc8483b88c8713a", "c813f88a3be3b334f75425ce9175fbe3c8493b89c8703b49"},
{"26dce334b545292f2feab9a8701a89a4b99eb9942cecd016", "00000001aa", "f58efc6f83f93e55e695fd252cf8fe59f7d5ba37ec", "f48ffd6e83f83e7354e694fd252cf83bfe58f7d5ba37ec5d"},
}
for _, test := range tests {
var e Des3CbcSha1Kd
key, _ := hex.DecodeString(test.key)
usage, _ := hex.DecodeString(test.usage)
derivedRandom, err := e.DeriveRandom(key, usage)
if err != nil {
t.Fatal(fmt.Sprintf("Error in deriveRandom: %v", err))
}
assert.Equal(t, test.dr, hex.EncodeToString(derivedRandom), "DR not as expected")
derivedKey, err := e.DeriveKey(key, usage)
if err != nil {
t.Fatal(fmt.Sprintf("Error in deriveKey: %v", err))
}
assert.Equal(t, test.dk, hex.EncodeToString(derivedKey), "DK not as expected")
}
}
func TestDes3CbcSha1Kd_StringToKey(t *testing.T) {
t.Parallel()
var tests = []struct {
salt string
secret string
key string
}{
{"ATHENA.MIT.EDUraeburn", "password", "850bb51358548cd05e86768c313e3bfef7511937dcf72c3e"},
{"WHITEHOUSE.GOVdanny", "potatoe", "dfcd233dd0a43204ea6dc437fb15e061b02979c1f74f377a"},
{"EXAMPLE.COMbuckaroo", "penny", "6d2fcdf2d6fbbc3ddcadb5da5710a23489b0d3b69d5d9d4a"},
{"ATHENA.MIT.EDUJuri" + "\u0161" + "i" + "\u0107", "\u00DF", "16d5a40e1ce3bacb61b9dce00470324c831973a7b952feb0"},
{"EXAMPLE.COMpianist", "𝄞", "85763726585dbc1cce6ec43e1f751f07f1c4cbb098f40b19"},
}
var e Des3CbcSha1Kd
for _, test := range tests {
key, err := e.StringToKey(test.secret, test.salt, "")
if err != nil {
t.Errorf("error in StringToKey: %v", err)
}
assert.Equal(t, test.key, hex.EncodeToString(key), "StringToKey not as expected")
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/etype/ 0000775 0000000 0000000 00000000000 13625372257 0023223 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/etype/etype.go 0000664 0000000 0000000 00000002120 13625372257 0024673 0 ustar 00root root 0000000 0000000 // Package etype provides the Kerberos Encryption Type interface
package etype
import "hash"
// EType is the interface defining the Encryption Type.
type EType interface {
GetETypeID() int32
GetHashID() int32
GetKeyByteSize() int
GetKeySeedBitLength() int
GetDefaultStringToKeyParams() string
StringToKey(string, salt, s2kparams string) ([]byte, error)
RandomToKey(b []byte) []byte
GetHMACBitLength() int
GetMessageBlockByteSize() int
EncryptData(key, data []byte) ([]byte, []byte, error)
EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error)
DecryptData(key, data []byte) ([]byte, error)
DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error)
GetCypherBlockBitLength() int
GetConfounderByteSize() int
DeriveKey(protocolKey, usage []byte) ([]byte, error)
DeriveRandom(protocolKey, usage []byte) ([]byte, error)
VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool
GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error)
VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool
GetHashFunc() func() hash.Hash
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rc4-hmac.go 0000664 0000000 0000000 00000010555 13625372257 0024030 0 ustar 00root root 0000000 0000000 package crypto
import (
"bytes"
"crypto/md5"
"hash"
"io"
"golang.org/x/crypto/md4"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3961"
"gopkg.in/jcmturner/gokrb5.v5/crypto/rfc4757"
"gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
)
//http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/security/krb5/internal/crypto/dk/ArcFourCrypto.java#ArcFourCrypto.encrypt%28byte%5B%5D%2Cint%2Cbyte%5B%5D%2Cbyte%5B%5D%2Cbyte%5B%5D%2Cint%2Cint%29
// RC4HMAC implements Kerberos encryption type aes256-cts-hmac-sha1-96
type RC4HMAC struct {
}
// GetETypeID returns the EType ID number.
func (e RC4HMAC) GetETypeID() int32 {
return etypeID.RC4_HMAC
}
// GetHashID returns the checksum type ID number.
func (e RC4HMAC) GetHashID() int32 {
return chksumtype.KERB_CHECKSUM_HMAC_MD5
}
// GetKeyByteSize returns the number of bytes for key of this etype.
func (e RC4HMAC) GetKeyByteSize() int {
return 16
}
// GetKeySeedBitLength returns the number of bits for the seed for key generation.
func (e RC4HMAC) GetKeySeedBitLength() int {
return e.GetKeyByteSize() * 8
}
// GetHashFunc returns the hash function for this etype.
func (e RC4HMAC) GetHashFunc() func() hash.Hash {
return md5.New
}
// GetMessageBlockByteSize returns the block size for the etype's messages.
func (e RC4HMAC) GetMessageBlockByteSize() int {
return 1
}
// GetDefaultStringToKeyParams returns the default key derivation parameters in string form.
func (e RC4HMAC) GetDefaultStringToKeyParams() string {
return ""
}
// GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations.
func (e RC4HMAC) GetConfounderByteSize() int {
return 8
}
// GetHMACBitLength returns the bit count size of the integrity hash.
func (e RC4HMAC) GetHMACBitLength() int {
return md5.Size * 8
}
// GetCypherBlockBitLength returns the bit count size of the cypher block.
func (e RC4HMAC) GetCypherBlockBitLength() int {
return 8 // doesn't really apply
}
// StringToKey returns a key derived from the string provided.
func (e RC4HMAC) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
return rfc4757.StringToKey(secret)
}
// RandomToKey returns a key from the bytes provided.
func (e RC4HMAC) RandomToKey(b []byte) []byte {
r := bytes.NewReader(b)
h := md4.New()
io.Copy(h, r)
return h.Sum(nil)
}
// EncryptData encrypts the data provided.
func (e RC4HMAC) EncryptData(key, data []byte) ([]byte, []byte, error) {
b, err := rfc4757.EncryptData(key, data, e)
return []byte{}, b, err
}
// EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message.
func (e RC4HMAC) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
b, err := rfc4757.EncryptMessage(key, message, usage, false, e)
return []byte{}, b, err
}
// DecryptData decrypts the data provided.
func (e RC4HMAC) DecryptData(key, data []byte) ([]byte, error) {
return rfc4757.DecryptData(key, data, e)
}
// DecryptMessage decrypts the message provided and verifies the integrity of the message.
func (e RC4HMAC) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
return rfc4757.DecryptMessage(key, ciphertext, usage, false, e)
}
// DeriveKey derives a key from the protocol key based on the usage value.
func (e RC4HMAC) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
return rfc4757.HMAC(protocolKey, usage), nil
}
// DeriveRandom generates data needed for key generation.
func (e RC4HMAC) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
return rfc3961.DeriveRandom(protocolKey, usage, e)
}
// VerifyIntegrity checks the integrity of the plaintext message.
func (e RC4HMAC) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {
return rfc4757.VerifyIntegrity(protocolKey, pt, ct, e)
}
// GetChecksumHash returns a keyed checksum hash of the bytes provided.
func (e RC4HMAC) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) {
return rfc4757.Checksum(protocolKey, usage, data)
}
// VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided.
func (e RC4HMAC) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool {
checksum, err := rfc4757.Checksum(protocolKey, usage, data)
if err != nil {
return false
}
if !bytes.Equal(checksum, chksum) {
return false
}
return true
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3961/ 0000775 0000000 0000000 00000000000 13625372257 0023172 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3961/encryption.go 0000664 0000000 0000000 00000010027 13625372257 0025713 0 ustar 00root root 0000000 0000000 // Package rfc3961 provides encryption and checksum methods as specified in RFC 3961
package rfc3961
import (
"crypto/cipher"
"crypto/des"
"crypto/hmac"
"crypto/rand"
"errors"
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/etype"
)
// DES3EncryptData encrypts the data provided using DES3 and methods specific to the etype provided.
func DES3EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) {
if len(key) != e.GetKeyByteSize() {
return nil, nil, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
}
data, _ = common.ZeroPad(data, e.GetMessageBlockByteSize())
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, nil, fmt.Errorf("error creating cipher: %v", err)
}
ivz := make([]byte, des.BlockSize)
ct := make([]byte, len(data))
mode := cipher.NewCBCEncrypter(block, ivz)
mode.CryptBlocks(ct, data)
return ct[len(ct)-e.GetMessageBlockByteSize():], ct, nil
}
// DES3EncryptMessage encrypts the message provided using DES3 and methods specific to the etype provided.
// The encrypted data is concatenated with its integrity hash to create an encrypted message.
func DES3EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) {
//confounder
c := make([]byte, e.GetConfounderByteSize())
_, err := rand.Read(c)
if err != nil {
return []byte{}, []byte{}, fmt.Errorf("could not generate random confounder: %v", err)
}
plainBytes := append(c, message...)
plainBytes, _ = common.ZeroPad(plainBytes, e.GetMessageBlockByteSize())
// Derive key for encryption from usage
var k []byte
if usage != 0 {
k, err = e.DeriveKey(key, common.GetUsageKe(usage))
if err != nil {
return []byte{}, []byte{}, fmt.Errorf("error deriving key for encryption: %v", err)
}
}
iv, b, err := e.EncryptData(k, plainBytes)
if err != nil {
return iv, b, fmt.Errorf("error encrypting data: %v", err)
}
// Generate and append integrity hash
ih, err := common.GetIntegrityHash(plainBytes, key, usage, e)
if err != nil {
return iv, b, fmt.Errorf("error encrypting data: %v", err)
}
b = append(b, ih...)
return iv, b, nil
}
// DES3DecryptData decrypts the data provided using DES3 and methods specific to the etype provided.
func DES3DecryptData(key, data []byte, e etype.EType) ([]byte, error) {
if len(key) != e.GetKeyByteSize() {
return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
}
if len(data) < des.BlockSize || len(data)%des.BlockSize != 0 {
return []byte{}, errors.New("ciphertext is not a multiple of the block size")
}
block, err := des.NewTripleDESCipher(key)
if err != nil {
return []byte{}, fmt.Errorf("error creating cipher: %v", err)
}
pt := make([]byte, len(data))
ivz := make([]byte, des.BlockSize)
mode := cipher.NewCBCDecrypter(block, ivz)
mode.CryptBlocks(pt, data)
return pt, nil
}
// DES3DecryptMessage decrypts the message provided using DES3 and methods specific to the etype provided.
// The integrity of the message is also verified.
func DES3DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) {
//Derive the key
k, err := e.DeriveKey(key, common.GetUsageKe(usage))
if err != nil {
return nil, fmt.Errorf("error deriving key: %v", err)
}
// Strip off the checksum from the end
b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8])
if err != nil {
return nil, fmt.Errorf("error decrypting: %v", err)
}
//Verify checksum
if !e.VerifyIntegrity(key, ciphertext, b, usage) {
return nil, errors.New("error decrypting: integrity verification failed")
}
//Remove the confounder bytes
return b[e.GetConfounderByteSize():], nil
}
// VerifyIntegrity verifies the integrity of cipertext bytes ct.
func VerifyIntegrity(key, ct, pt []byte, usage uint32, etype etype.EType) bool {
h := make([]byte, etype.GetHMACBitLength()/8)
copy(h, ct[len(ct)-etype.GetHMACBitLength()/8:])
expectedMAC, _ := common.GetIntegrityHash(pt, key, usage, etype)
return hmac.Equal(h, expectedMAC)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3961/keyDerivation.go 0000664 0000000 0000000 00000006444 13625372257 0026346 0 ustar 00root root 0000000 0000000 package rfc3961
import (
"gopkg.in/jcmturner/gokrb5.v5/crypto/etype"
)
const (
prfconstant = "prf"
)
// DeriveRandom implements the RFC 3961 defined function
//
// key: base key or protocol key. Likely to be a key from a keytab file.
//
// usage: a constant.
//
// n: block size in bits (not bytes) - note if you use something like aes.BlockSize this is in bytes.
//
// k: key length / key seed length in bits. Eg. for AES256 this value is 256.
//
// e: the encryption etype function to use.
func DeriveRandom(key, usage []byte, e etype.EType) ([]byte, error) {
n := e.GetCypherBlockBitLength()
k := e.GetKeySeedBitLength()
//Ensure the usage constant is at least the size of the cypher block size. Pass it through the nfold algorithm that will "stretch" it if needs be.
nFoldUsage := Nfold(usage, n)
//k-truncate implemented by creating a byte array the size of k (k is in bits hence /8)
out := make([]byte, k/8)
_, K, err := e.EncryptData(key, nFoldUsage)
if err != nil {
return out, err
}
for i := copy(out, K); i < len(out); {
_, K, _ = e.EncryptData(key, K)
i = i + copy(out[i:], K)
}
return out, nil
}
// DeriveKey derives a key from the protocol key based on the usage and the etype's specific methods.
func DeriveKey(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
r, err := e.DeriveRandom(protocolKey, usage)
if err != nil {
return nil, err
}
return e.RandomToKey(r), nil
}
// RandomToKey returns a key from the bytes provided according to the definition in RFC 3961.
func RandomToKey(b []byte) []byte {
return b
}
// DES3RandomToKey returns a key from the bytes provided according to the definition in RFC 3961 for DES3 etypes.
func DES3RandomToKey(b []byte) []byte {
r := stretch56Bits(b[:7])
r2 := stretch56Bits(b[7:14])
r = append(r, r2...)
r3 := stretch56Bits(b[14:21])
r = append(r, r3...)
return r
}
// DES3StringToKey returns a key derived from the string provided according to the definition in RFC 3961 for DES3 etypes.
func DES3StringToKey(secret, salt string, e etype.EType) ([]byte, error) {
s := secret + salt
tkey := e.RandomToKey(Nfold([]byte(s), e.GetKeySeedBitLength()))
return e.DeriveKey(tkey, []byte("kerberos"))
}
// PseudoRandom function as defined in RFC 3961
func PseudoRandom(key, b []byte, e etype.EType) ([]byte, error) {
h := e.GetHashFunc()()
h.Write(b)
tmp := h.Sum(nil)[:e.GetMessageBlockByteSize()]
k, err := e.DeriveKey(key, []byte(prfconstant))
if err != nil {
return []byte{}, err
}
_, prf, err := e.EncryptData(k, tmp)
if err != nil {
return []byte{}, err
}
return prf, nil
}
func stretch56Bits(b []byte) []byte {
d := make([]byte, len(b), len(b))
copy(d, b)
var lb byte
for i, v := range d {
bv, nb := calcEvenParity(v)
d[i] = nb
if bv != 0 {
lb = lb | (1 << uint(i+1))
} else {
lb = lb &^ (1 << uint(i+1))
}
}
_, lb = calcEvenParity(lb)
d = append(d, lb)
return d
}
func calcEvenParity(b byte) (uint8, uint8) {
lowestbit := b & 0x01
// c counter of 1s in the first 7 bits of the byte
var c int
// Iterate over the highest 7 bits (hence p starts at 1 not zero) and count the 1s.
for p := 1; p < 8; p++ {
v := b & (1 << uint(p))
if v != 0 {
c++
}
}
if c%2 == 0 {
//Even number of 1s so set parity to 1
b = b | 1
} else {
//Odd number of 1s so set parity to 0
b = b &^ 1
}
return lowestbit, b
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3961/nfold.go 0000664 0000000 0000000 00000005077 13625372257 0024634 0 ustar 00root root 0000000 0000000 package rfc3961
/* Credits
This golang implementation of nfold used the following project for help with implementation detail.
Although their source is in java it was helpful as a reference implementation of the RFC.
You can find the source code of their open source project along with license information below.
We acknowledge and are grateful to these developers for their contributions to open source
Project: Apache Directory (http://http://directory.apache.org/)
https://svn.apache.org/repos/asf/directory/apacheds/tags/1.5.1/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NFold.java
License: http://www.apache.org/licenses/LICENSE-2.0
*/
// Nfold expands the key to ensure it is not smaller than one cipher block.
// Defined in RFC 3961.
//
// m input bytes that will be "stretched" to the least common multiple of n bits and the bit length of m.
func Nfold(m []byte, n int) []byte {
k := len(m) * 8
//Get the lowest common multiple of the two bit sizes
lcm := lcm(n, k)
relicate := lcm / k
var sumBytes []byte
for i := 0; i < relicate; i++ {
rotation := 13 * i
sumBytes = append(sumBytes, rotateRight(m, rotation)...)
}
nfold := make([]byte, n/8)
sum := make([]byte, n/8)
for i := 0; i < lcm/n; i++ {
for j := 0; j < n/8; j++ {
sum[j] = sumBytes[j+(i*len(sum))]
}
nfold = onesComplementAddition(nfold, sum)
}
return nfold
}
func onesComplementAddition(n1, n2 []byte) []byte {
numBits := len(n1) * 8
out := make([]byte, numBits/8)
carry := 0
for i := numBits - 1; i > -1; i-- {
n1b := getBit(&n1, i)
n2b := getBit(&n2, i)
s := n1b + n2b + carry
if s == 0 || s == 1 {
setBit(&out, i, s)
carry = 0
} else if s == 2 {
carry = 1
} else if s == 3 {
setBit(&out, i, 1)
carry = 1
}
}
if carry == 1 {
carryArray := make([]byte, len(n1))
carryArray[len(carryArray)-1] = 1
out = onesComplementAddition(out, carryArray)
}
return out
}
func rotateRight(b []byte, step int) []byte {
out := make([]byte, len(b))
bitLen := len(b) * 8
for i := 0; i < bitLen; i++ {
v := getBit(&b, i)
setBit(&out, (i+step)%bitLen, v)
}
return out
}
func lcm(x, y int) int {
return (x * y) / gcd(x, y)
}
func gcd(x, y int) int {
for y != 0 {
x, y = y, x%y
}
return x
}
func getBit(b *[]byte, p int) int {
pByte := p / 8
pBit := uint(p % 8)
vByte := (*b)[pByte]
vInt := int(vByte >> (8 - (pBit + 1)) & 0x0001)
return vInt
}
func setBit(b *[]byte, p, v int) {
pByte := p / 8
pBit := uint(p % 8)
oldByte := (*b)[pByte]
var newByte byte
newByte = byte(v<<(8-(pBit+1))) | oldByte
(*b)[pByte] = newByte
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3961/nfold_test.go 0000664 0000000 0000000 00000001507 13625372257 0025665 0 ustar 00root root 0000000 0000000 package rfc3961
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_nfold(t *testing.T) {
t.Parallel()
var tests = []struct {
n int
b []byte
folded string
}{
{64, []byte("012345"), "be072631276b1955"},
{56, []byte("password"), "78a07b6caf85fa"},
{64, []byte("Rough Consensus, and Running Code"), "bb6ed30870b7f0e0"},
{168, []byte("password"), "59e4a8ca7c0385c3c37b3f6d2000247cb6e6bd5b3e"},
{192, []byte("MASSACHVSETTS INSTITVTE OF TECHNOLOGY"), "db3b0d8f0b061e603282b308a50841229ad798fab9540c1b"},
{168, []byte("Q"), "518a54a215a8452a518a54a215a8452a518a54a215"},
{168, []byte("ba"), "fb25d531ae8974499f52fd92ea9857c4ba24cf297e"},
}
for _, test := range tests {
assert.Equal(t, test.folded, hex.EncodeToString(Nfold(test.b, test.n)), "Folded not as expected")
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3962/ 0000775 0000000 0000000 00000000000 13625372257 0023173 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3962/encryption.go 0000664 0000000 0000000 00000006222 13625372257 0025716 0 ustar 00root root 0000000 0000000 // Package rfc3962 provides encryption and checksum methods as specified in RFC 3962
package rfc3962
import (
"crypto/rand"
"errors"
"fmt"
"gopkg.in/jcmturner/aescts.v1"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/etype"
)
// EncryptData encrypts the data provided using methods specific to the etype provided as defined in RFC 3962.
func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) {
if len(key) != e.GetKeyByteSize() {
return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
}
ivz := make([]byte, e.GetCypherBlockBitLength()/8)
return aescts.Encrypt(key, ivz, data)
}
// EncryptMessage encrypts the message provided using the methods specific to the etype provided as defined in RFC 3962.
// The encrypted data is concatenated with its integrity hash to create an encrypted message.
func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) {
if len(key) != e.GetKeyByteSize() {
return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
}
//confounder
c := make([]byte, e.GetConfounderByteSize())
_, err := rand.Read(c)
if err != nil {
return []byte{}, []byte{}, fmt.Errorf("could not generate random confounder: %v", err)
}
plainBytes := append(c, message...)
// Derive key for encryption from usage
var k []byte
if usage != 0 {
k, err = e.DeriveKey(key, common.GetUsageKe(usage))
if err != nil {
return []byte{}, []byte{}, fmt.Errorf("error deriving key for encryption: %v", err)
}
}
// Encrypt the data
iv, b, err := e.EncryptData(k, plainBytes)
if err != nil {
return iv, b, fmt.Errorf("error encrypting data: %v", err)
}
// Generate and append integrity hash
ih, err := common.GetIntegrityHash(plainBytes, key, usage, e)
if err != nil {
return iv, b, fmt.Errorf("error encrypting data: %v", err)
}
b = append(b, ih...)
return iv, b, nil
}
// DecryptData decrypts the data provided using the methods specific to the etype provided as defined in RFC 3962.
func DecryptData(key, data []byte, e etype.EType) ([]byte, error) {
if len(key) != e.GetKeyByteSize() {
return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
}
ivz := make([]byte, e.GetCypherBlockBitLength()/8)
return aescts.Decrypt(key, ivz, data)
}
// DecryptMessage decrypts the message provided using the methods specific to the etype provided as defined in RFC 3962.
// The integrity of the message is also verified.
func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) {
//Derive the key
k, err := e.DeriveKey(key, common.GetUsageKe(usage))
if err != nil {
return nil, fmt.Errorf("error deriving key: %v", err)
}
// Strip off the checksum from the end
b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8])
if err != nil {
return nil, err
}
//Verify checksum
if !e.VerifyIntegrity(key, ciphertext, b, usage) {
return nil, errors.New("integrity verification failed")
}
//Remove the confounder bytes
return b[e.GetConfounderByteSize():], nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3962/keyDerivation.go 0000664 0000000 0000000 00000003470 13625372257 0026343 0 ustar 00root root 0000000 0000000 package rfc3962
import (
"encoding/binary"
"encoding/hex"
"errors"
"github.com/jcmturner/gofork/x/crypto/pbkdf2"
"gopkg.in/jcmturner/gokrb5.v5/crypto/etype"
)
const (
s2kParamsZero = 4294967296
)
// StringToKey returns a key derived from the string provided according to the definition in RFC 3961.
func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) {
i, err := S2KparamsToItertions(s2kparams)
if err != nil {
return nil, err
}
return StringToKeyIter(secret, salt, i, e)
}
// StringToPBKDF2 generates an encryption key from a pass phrase and salt string using the PBKDF2 function from PKCS #5 v2.0
func StringToPBKDF2(secret, salt string, iterations int64, e etype.EType) []byte {
return pbkdf2.Key64([]byte(secret), []byte(salt), iterations, int64(e.GetKeyByteSize()), e.GetHashFunc())
}
// StringToKeyIter returns a key derived from the string provided according to the definition in RFC 3961.
func StringToKeyIter(secret, salt string, iterations int64, e etype.EType) ([]byte, error) {
tkey := e.RandomToKey(StringToPBKDF2(secret, salt, iterations, e))
return e.DeriveKey(tkey, []byte("kerberos"))
}
// S2KparamsToItertions converts the string representation of iterations to an integer
func S2KparamsToItertions(s2kparams string) (int64, error) {
//process s2kparams string
var i uint32
if len(s2kparams) != 8 {
return int64(s2kParamsZero), errors.New("invalid s2kparams length")
}
b, err := hex.DecodeString(s2kparams)
if err != nil {
return int64(s2kParamsZero), errors.New("invalid s2kparams, cannot decode string to bytes")
}
i = binary.BigEndian.Uint32(b)
//buf := bytes.NewBuffer(b)
//err = binary.Read(buf, binary.BigEndian, &i)
if err != nil {
return int64(s2kParamsZero), errors.New("invalid s2kparams, cannot convert to big endian int32")
}
return int64(i), nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/ 0000775 0000000 0000000 00000000000 13625372257 0023176 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/checksum.go 0000664 0000000 0000000 00000001514 13625372257 0025330 0 ustar 00root root 0000000 0000000 package rfc4757
import (
"bytes"
"crypto/hmac"
"crypto/md5"
"io"
)
// Checksum returns a hash of the data in accordance with RFC 4757
func Checksum(key []byte, usage uint32, data []byte) ([]byte, error) {
// Create hashing key
s := append([]byte(`signaturekey`), byte(0x00)) //includes zero octet at end
mac := hmac.New(md5.New, key)
mac.Write(s)
Ksign := mac.Sum(nil)
// Format data
tb := UsageToMSMsgType(usage)
p := append(tb, data...)
h := md5.New()
rb := bytes.NewReader(p)
_, err := io.Copy(h, rb)
if err != nil {
return []byte{}, err
}
tmp := h.Sum(nil)
// Generate HMAC
mac = hmac.New(md5.New, Ksign)
mac.Write(tmp)
return mac.Sum(nil), nil
}
// HMAC returns a keyed MD5 checksum of the data
func HMAC(key []byte, data []byte) []byte {
mac := hmac.New(md5.New, key)
mac.Write(data)
return mac.Sum(nil)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/encryption.go 0000664 0000000 0000000 00000005415 13625372257 0025724 0 ustar 00root root 0000000 0000000 // Package rfc4757 provides encryption and checksum methods as specified in RFC 4757
package rfc4757
import (
"bytes"
"crypto/rand"
"crypto/rc4"
"errors"
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/crypto/etype"
)
// EncryptData encrypts the data provided using methods specific to the etype provided as defined in RFC 4757.
func EncryptData(key, data []byte, e etype.EType) ([]byte, error) {
if len(key) != e.GetKeyByteSize() {
return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
}
rc4Cipher, err := rc4.NewCipher(key)
if err != nil {
return []byte{}, fmt.Errorf("error creating RC4 cipher: %v", err)
}
ed := make([]byte, len(data))
copy(ed, data)
rc4Cipher.XORKeyStream(ed, ed)
rc4Cipher.Reset()
return ed, nil
}
// DecryptData decrypts the data provided using the methods specific to the etype provided as defined in RFC 4757.
func DecryptData(key, data []byte, e etype.EType) ([]byte, error) {
return EncryptData(key, data, e)
}
// EncryptMessage encrypts the message provided using the methods specific to the etype provided as defined in RFC 4757.
// The encrypted data is concatenated with its RC4 header containing integrity checksum and confounder to create an encrypted message.
func EncryptMessage(key, data []byte, usage uint32, export bool, e etype.EType) ([]byte, error) {
confounder := make([]byte, e.GetConfounderByteSize()) // size = 8
_, err := rand.Read(confounder)
if err != nil {
return []byte{}, fmt.Errorf("error generating confounder: %v", err)
}
k1 := key
k2 := HMAC(k1, UsageToMSMsgType(usage))
toenc := append(confounder, data...)
chksum := HMAC(k2, toenc)
k3 := HMAC(k2, chksum)
ed, err := EncryptData(k3, toenc, e)
if err != nil {
return []byte{}, fmt.Errorf("error encrypting data: %v", err)
}
msg := append(chksum, ed...)
return msg, nil
}
// DecryptMessage decrypts the message provided using the methods specific to the etype provided as defined in RFC 4757.
// The integrity of the message is also verified.
func DecryptMessage(key, data []byte, usage uint32, export bool, e etype.EType) ([]byte, error) {
checksum := data[:e.GetHMACBitLength()/8]
ct := data[e.GetHMACBitLength()/8:]
_, k2, k3 := deriveKeys(key, checksum, usage, export)
pt, err := DecryptData(k3, ct, e)
if err != nil {
return []byte{}, fmt.Errorf("error decrypting data: %v", err)
}
if !VerifyIntegrity(k2, pt, data, e) {
return []byte{}, errors.New("integrity checksum incorrect")
}
return pt[e.GetConfounderByteSize():], nil
}
// VerifyIntegrity checks the integrity checksum of the data matches that calculated from the decrypted data.
func VerifyIntegrity(key, pt, data []byte, e etype.EType) bool {
chksum := HMAC(key, pt)
if bytes.Equal(chksum, data[:e.GetHMACBitLength()/8]) {
return true
}
return false
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/keyDerivation.go 0000664 0000000 0000000 00000002342 13625372257 0026343 0 ustar 00root root 0000000 0000000 package rfc4757
import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"io"
"golang.org/x/crypto/md4"
)
// StringToKey returns a key derived from the string provided according to the definition in RFC 4757.
func StringToKey(secret string) ([]byte, error) {
b := make([]byte, len(secret)*2, len(secret)*2)
for i, r := range secret {
u := fmt.Sprintf("%04x", r)
c, err := hex.DecodeString(u)
if err != nil {
return []byte{}, errors.New("character could not be encoded")
}
// Swap round the two bytes to make little endian as we put into byte slice
b[2*i] = c[1]
b[2*i+1] = c[0]
}
r := bytes.NewReader(b)
h := md4.New()
_, err := io.Copy(h, r)
if err != nil {
return []byte{}, err
}
return h.Sum(nil), nil
}
func deriveKeys(key, checksum []byte, usage uint32, export bool) (k1, k2, k3 []byte) {
//if export {
// L40 := make([]byte, 14, 14)
// copy(L40, []byte(`fortybits`))
// k1 = HMAC(key, L40)
//} else {
// tb := MessageTypeBytes(usage)
// k1 = HMAC(key, tb)
//}
//k2 = k1[:16]
//if export {
// mask := []byte{0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB}
// copy(k1[7:16], mask)
//}
//k3 = HMAC(k1, checksum)
//return
k1 = key
k2 = HMAC(k1, UsageToMSMsgType(usage))
k3 = HMAC(k2, checksum)
return
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/keyDerivation_test.go 0000664 0000000 0000000 00000000651 13625372257 0027403 0 ustar 00root root 0000000 0000000 package rfc4757
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
)
const (
testPassword = "foo"
testKey = "ac8e657f83df82beea5d43bdaf7800cc"
)
func TestStringToKey(t *testing.T) {
t.Parallel()
kb, err := StringToKey(testPassword)
if err != nil {
t.Fatalf("Error deriving key from string: %v", err)
}
k := hex.EncodeToString(kb)
assert.Equal(t, testKey, k, "Key not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/msgtype.go 0000664 0000000 0000000 00000000773 13625372257 0025224 0 ustar 00root root 0000000 0000000 package rfc4757
import "encoding/binary"
// UsageToMSMsgType converts Kerberos key usage numbers to Microsoft message type encoded as a little-endian four byte slice.
func UsageToMSMsgType(usage uint32) []byte {
// Translate usage numbers to the Microsoft T numbers
switch usage {
case 3:
usage = 8
case 9:
usage = 8
case 23:
usage = 13
}
// Now convert to bytes
tb := make([]byte, 4) // We force an int32 input so we can't go over 4 bytes
binary.PutUvarint(tb, uint64(usage))
return tb
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc8009/ 0000775 0000000 0000000 00000000000 13625372257 0023170 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc8009/encryption.go 0000664 0000000 0000000 00000010256 13625372257 0025715 0 ustar 00root root 0000000 0000000 // Package rfc8009 provides encryption and checksum methods as specified in RFC 8009
package rfc8009
import (
"crypto/aes"
"crypto/hmac"
"crypto/rand"
"errors"
"fmt"
"gopkg.in/jcmturner/aescts.v1"
"gopkg.in/jcmturner/gokrb5.v5/crypto/common"
"gopkg.in/jcmturner/gokrb5.v5/crypto/etype"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
)
// EncryptData encrypts the data provided using methods specific to the etype provided as defined in RFC 8009.
func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) {
kl := e.GetKeyByteSize()
if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
kl = 32
}
if len(key) != kl {
return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
}
ivz := make([]byte, aes.BlockSize)
return aescts.Encrypt(key, ivz, data)
}
// EncryptMessage encrypts the message provided using the methods specific to the etype provided as defined in RFC 8009.
// The encrypted data is concatenated with its integrity hash to create an encrypted message.
func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) {
kl := e.GetKeyByteSize()
if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
kl = 32
}
if len(key) != kl {
return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", kl, len(key))
}
if len(key) != e.GetKeyByteSize() {
}
//confounder
c := make([]byte, e.GetConfounderByteSize())
_, err := rand.Read(c)
if err != nil {
return []byte{}, []byte{}, fmt.Errorf("could not generate random confounder: %v", err)
}
plainBytes := append(c, message...)
// Derive key for encryption from usage
var k []byte
if usage != 0 {
k, err = e.DeriveKey(key, common.GetUsageKe(usage))
if err != nil {
return []byte{}, []byte{}, fmt.Errorf("error deriving key for encryption: %v", err)
}
}
// Encrypt the data
iv, b, err := e.EncryptData(k, plainBytes)
if err != nil {
return iv, b, fmt.Errorf("error encrypting data: %v", err)
}
ivz := make([]byte, e.GetConfounderByteSize())
ih, err := GetIntegityHash(ivz, b, key, usage, e)
if err != nil {
return iv, b, fmt.Errorf("error encrypting data: %v", err)
}
b = append(b, ih...)
return iv, b, nil
}
// DecryptData decrypts the data provided using the methods specific to the etype provided as defined in RFC 8009.
func DecryptData(key, data []byte, e etype.EType) ([]byte, error) {
kl := e.GetKeyByteSize()
if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
kl = 32
}
if len(key) != kl {
return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", kl, len(key))
}
ivz := make([]byte, aes.BlockSize)
return aescts.Decrypt(key, ivz, data)
}
// DecryptMessage decrypts the message provided using the methods specific to the etype provided as defined in RFC 8009.
// The integrity of the message is also verified.
func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) {
//Derive the key
k, err := e.DeriveKey(key, common.GetUsageKe(usage))
if err != nil {
return nil, fmt.Errorf("error deriving key: %v", err)
}
// Strip off the checksum from the end
b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8])
if err != nil {
return nil, err
}
//Verify checksum
if !e.VerifyIntegrity(key, ciphertext, b, usage) {
return nil, errors.New("integrity verification failed")
}
//Remove the confounder bytes
return b[e.GetConfounderByteSize():], nil
}
// GetIntegityHash returns a keyed integrity hash of the bytes provided as defined in RFC 8009
func GetIntegityHash(iv, c, key []byte, usage uint32, e etype.EType) ([]byte, error) {
// Generate and append integrity hash
ib := append(iv, c...)
return common.GetIntegrityHash(ib, key, usage, e)
}
// VerifyIntegrity verifies the integrity of cipertext bytes ct.
func VerifyIntegrity(key, ct []byte, usage uint32, etype etype.EType) bool {
h := make([]byte, etype.GetHMACBitLength()/8)
copy(h, ct[len(ct)-etype.GetHMACBitLength()/8:])
ivz := make([]byte, etype.GetConfounderByteSize())
ib := append(ivz, ct[:len(ct)-(etype.GetHMACBitLength()/8)]...)
expectedMAC, _ := common.GetIntegrityHash(ib, key, usage, etype)
return hmac.Equal(h, expectedMAC)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc8009/keyDerivation.go 0000664 0000000 0000000 00000010155 13625372257 0026336 0 ustar 00root root 0000000 0000000 package rfc8009
import (
"crypto/hmac"
"encoding/binary"
"encoding/hex"
"errors"
"golang.org/x/crypto/pbkdf2"
"gopkg.in/jcmturner/gokrb5.v5/crypto/etype"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
)
const (
s2kParamsZero = 32768
)
// DeriveRandom for key derivation as defined in RFC 8009
func DeriveRandom(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
h := e.GetHashFunc()()
return KDF_HMAC_SHA2(protocolKey, []byte("prf"), usage, h.Size(), e), nil
}
// DeriveKey derives a key from the protocol key based on the usage and the etype's specific methods.
//
// https://tools.ietf.org/html/rfc8009#section-5
func DeriveKey(protocolKey, label []byte, e etype.EType) []byte {
var context []byte
var kl int
// Key length is longer for aes256-cts-hmac-sha384-192 is it is a Ke or from StringToKey (where label is "kerberos")
if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
switch label[len(label)-1] {
case 0x73:
// 0x73 is "s" so label could be kerberos meaning StringToKey so now check if the label is "kerberos"
kerblabel := []byte("kerberos")
if len(label) != len(kerblabel) {
break
}
for i, b := range label {
if b != kerblabel[i] {
kl = e.GetKeySeedBitLength()
break
}
}
if kl == 0 {
// This is StringToKey
kl = 256
}
case 0xAA:
// This is a Ke
kl = 256
}
}
if kl == 0 {
kl = e.GetKeySeedBitLength()
}
return e.RandomToKey(KDF_HMAC_SHA2(protocolKey, label, context, kl, e))
}
// RandomToKey returns a key from the bytes provided according to the definition in RFC 8009.
func RandomToKey(b []byte) []byte {
return b
}
// StringToKey returns a key derived from the string provided according to the definition in RFC 8009.
func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) {
i, err := S2KparamsToItertions(s2kparams)
if err != nil {
return nil, err
}
return StringToKeyIter(secret, salt, int(i), e)
}
// StringToKeyIter returns a key derived from the string provided according to the definition in RFC 8009.
func StringToKeyIter(secret, salt string, iterations int, e etype.EType) ([]byte, error) {
tkey := e.RandomToKey(StringToPBKDF2(secret, salt, iterations, e))
return e.DeriveKey(tkey, []byte("kerberos"))
}
// StringToPBKDF2 generates an encryption key from a pass phrase and salt string using the PBKDF2 function from PKCS #5 v2.0
func StringToPBKDF2(secret, salt string, iterations int, e etype.EType) []byte {
kl := e.GetKeyByteSize()
if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
kl = 32
}
return pbkdf2.Key([]byte(secret), []byte(salt), iterations, kl, e.GetHashFunc())
}
// KDF_HMAC_SHA2 key derivation: https://tools.ietf.org/html/rfc8009#section-3
func KDF_HMAC_SHA2(protocolKey, label, context []byte, kl int, e etype.EType) []byte {
//k: Length in bits of the key to be outputted, expressed in big-endian binary representation in 4 bytes.
k := make([]byte, 4, 4)
binary.BigEndian.PutUint32(k, uint32(kl))
c := make([]byte, 4, 4)
binary.BigEndian.PutUint32(c, uint32(1))
c = append(c, label...)
c = append(c, byte(uint8(0)))
if len(context) > 0 {
c = append(c, context...)
}
c = append(c, k...)
mac := hmac.New(e.GetHashFunc(), protocolKey)
mac.Write(c)
return mac.Sum(nil)[:(kl / 8)]
}
// GetSaltP returns the salt value based on the etype name: https://tools.ietf.org/html/rfc8009#section-4
func GetSaltP(salt, ename string) string {
b := []byte(ename)
b = append(b, byte(uint8(0)))
b = append(b, []byte(salt)...)
return string(b)
}
// S2KparamsToItertions converts the string representation of iterations to an integer for RFC 8009.
func S2KparamsToItertions(s2kparams string) (int, error) {
var i uint32
if len(s2kparams) != 8 {
return s2kParamsZero, errors.New("Invalid s2kparams length")
}
b, err := hex.DecodeString(s2kparams)
if err != nil {
return s2kParamsZero, errors.New("Invalid s2kparams, cannot decode string to bytes")
}
i = binary.BigEndian.Uint32(b)
//buf := bytes.NewBuffer(b)
//err = binary.Read(buf, binary.BigEndian, &i)
if err != nil {
return s2kParamsZero, errors.New("Invalid s2kparams, cannot convert to big endian int32")
}
return int(i), nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/examples/ 0000775 0000000 0000000 00000000000 13625372257 0022373 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/examples/example-AD.go 0000664 0000000 0000000 00000007242 13625372257 0024644 0 ustar 00root root 0000000 0000000 // +build examples
package main
import (
"encoding/hex"
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/client"
"gopkg.in/jcmturner/gokrb5.v5/config"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/service"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"os"
)
func main() {
s := httpServer()
defer s.Close()
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
cl := client.NewClientWithKeytab("testuser1", "USER.GOKRB5", kt)
cl.WithConfig(c)
httpRequest(s.URL, cl)
b, _ = hex.DecodeString(testdata.TESTUSER2_KEYTAB)
kt, _ = keytab.Parse(b)
c, _ = config.NewConfigFromString(testdata.TEST_KRB5CONF)
cl = client.NewClientWithKeytab("testuser2", "USER.GOKRB5", kt)
cl.WithConfig(c)
httpRequest(s.URL, cl)
//httpRequest("http://host.test.gokrb5/index.html")
}
func httpRequest(url string, cl client.Client) {
l := log.New(os.Stderr, "GOKRB5 Client: ", log.Ldate|log.Ltime|log.Lshortfile)
err := cl.Login()
if err != nil {
l.Printf("Error on AS_REQ: %v\n", err)
}
r, _ := http.NewRequest("GET", url, nil)
err = cl.SetSPNEGOHeader(r, "HTTP/host.res.gokrb5")
if err != nil {
l.Printf("Error setting client SPNEGO header: %v", err)
}
httpResp, err := http.DefaultClient.Do(r)
if err != nil {
l.Printf("Request error: %v\n", err)
}
fmt.Fprintf(os.Stdout, "Response Code: %v\n", httpResp.StatusCode)
content, _ := ioutil.ReadAll(httpResp.Body)
fmt.Fprintf(os.Stdout, "Response Body:\n%s\n", content)
}
func httpServer() *httptest.Server {
l := log.New(os.Stderr, "GOKRB5 Service: ", log.Ldate|log.Ltime|log.Lshortfile)
b, _ := hex.DecodeString(testdata.SYSHTTP_KEYTAB)
kt, _ := keytab.Parse(b)
th := http.HandlerFunc(testAppHandler)
s := httptest.NewServer(service.SPNEGOKRB5Authenticate(th, kt, "sysHTTP", false, l))
return s
}
func testAppHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
fmt.Fprint(w, "\nTEST.GOKRB5 Handler
\n")
if validuser, ok := ctx.Value(service.CTXKeyAuthenticated).(bool); ok && validuser {
if creds, ok := ctx.Value(service.CTXKeyCredentials).(credentials.Credentials); ok {
fmt.Fprintf(w, "- Authenticed user: %s
\n", creds.UserName())
fmt.Fprintf(w, "- User's realm: %s
\n", creds.Domain())
fmt.Fprint(w, "- Authz Attributes (Group Memberships):
\n")
for _, s := range creds.AuthzAttributes() {
fmt.Fprintf(w, "- %v
\n", s)
}
fmt.Fprint(w, "
\n")
if ADCreds, ok := creds.Attributes[credentials.AttributeKeyADCredentials].(credentials.ADCredentials); ok {
// Now access the fields of the ADCredentials struct. For example:
fmt.Fprintf(w, "- EffectiveName: %v
\n", ADCreds.EffectiveName)
fmt.Fprintf(w, "- FullName: %v
\n", ADCreds.FullName)
fmt.Fprintf(w, "- UserID: %v
\n", ADCreds.UserID)
fmt.Fprintf(w, "- PrimaryGroupID: %v
\n", ADCreds.PrimaryGroupID)
fmt.Fprintf(w, "- Group SIDs: %v
\n", ADCreds.GroupMembershipSIDs)
fmt.Fprintf(w, "- LogOnTime: %v
\n", ADCreds.LogOnTime)
fmt.Fprintf(w, "- LogOffTime: %v
\n", ADCreds.LogOffTime)
fmt.Fprintf(w, "- PasswordLastSet: %v
\n", ADCreds.PasswordLastSet)
fmt.Fprintf(w, "- LogonServer: %v
\n", ADCreds.LogonServer)
fmt.Fprintf(w, "- LogonDomainName: %v
\n", ADCreds.LogonDomainName)
fmt.Fprintf(w, "- LogonDomainID: %v
\n", ADCreds.LogonDomainID)
}
fmt.Fprint(w, "
")
}
} else {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprint(w, "Authentication failed")
}
fmt.Fprint(w, "")
return
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/examples/example.go 0000664 0000000 0000000 00000005001 13625372257 0024351 0 ustar 00root root 0000000 0000000 // +build examples
package main
import (
"encoding/hex"
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/client"
"gopkg.in/jcmturner/gokrb5.v5/config"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/service"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"os"
)
func main() {
s := httpServer()
defer s.Close()
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
c.LibDefaults.NoAddresses = true
cl := client.NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
httpRequest(s.URL, cl)
b, _ = hex.DecodeString(testdata.TESTUSER2_KEYTAB)
kt, _ = keytab.Parse(b)
c, _ = config.NewConfigFromString(testdata.TEST_KRB5CONF)
c.LibDefaults.NoAddresses = true
cl = client.NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
cl.WithConfig(c)
httpRequest(s.URL, cl)
}
func httpRequest(url string, cl client.Client) {
l := log.New(os.Stderr, "GOKRB5 Client: ", log.Ldate|log.Ltime|log.Lshortfile)
err := cl.Login()
if err != nil {
l.Printf("Error on AS_REQ: %v\n", err)
}
r, _ := http.NewRequest("GET", url, nil)
err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
if err != nil {
l.Printf("Error setting client SPNEGO header: %v", err)
}
httpResp, err := http.DefaultClient.Do(r)
if err != nil {
l.Printf("Request error: %v\n", err)
}
fmt.Fprintf(os.Stdout, "Response Code: %v\n", httpResp.StatusCode)
content, _ := ioutil.ReadAll(httpResp.Body)
fmt.Fprintf(os.Stdout, "Response Body:\n%s\n", content)
}
func httpServer() *httptest.Server {
l := log.New(os.Stderr, "GOKRB5 Service: ", log.Ldate|log.Ltime|log.Lshortfile)
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
th := http.HandlerFunc(testAppHandler)
s := httptest.NewServer(service.SPNEGOKRB5Authenticate(th, kt, "", false, l))
return s
}
func testAppHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
fmt.Fprint(w, "\nTEST.GOKRB5 Handler
\n")
if validuser, ok := ctx.Value(service.CTXKeyAuthenticated).(bool); ok && validuser {
if creds, ok := ctx.Value(service.CTXKeyCredentials).(credentials.Credentials); ok {
fmt.Fprintf(w, "- Authenticed user: %s
\n", creds.Username)
fmt.Fprintf(w, "- User's realm: %s
\n", creds.Realm)
}
} else {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprint(w, "Authentication failed")
}
fmt.Fprint(w, "")
return
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/examples/httpClient.go 0000664 0000000 0000000 00000003521 13625372257 0025041 0 ustar 00root root 0000000 0000000 // +build examples
package main
import (
"encoding/hex"
"fmt"
"io/ioutil"
"net/http"
"os"
//"github.com/pkg/profile"
"gopkg.in/jcmturner/gokrb5.v5/client"
"gopkg.in/jcmturner/gokrb5.v5/config"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
const (
port = ":9080"
kRB5CONF = `[libdefaults]
default_realm = TEST.GOKRB5
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
forwardable = yes
default_tkt_enctypes = aes256-cts-hmac-sha1-96
default_tgs_enctypes = aes256-cts-hmac-sha1-96
[realms]
TEST.GOKRB5 = {
kdc = 127.0.0.1:88
admin_server = 127.0.0.1:749
default_domain = test.gokrb5
}
[domain_realm]
.test.gokrb5 = TEST.GOKRB5
test.gokrb5 = TEST.GOKRB5
`
)
func main() {
//defer profile.Start(profile.TraceProfile).Stop()
// Load the keytab
kb, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, err := keytab.Parse(kb)
if err != nil {
panic(err)
}
// Create the client with the keytab
cl := client.NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
// Load the client krb5 config
conf, err := config.NewConfigFromString(kRB5CONF)
if err != nil {
panic(err)
}
addr := os.Getenv("TEST_KDC_ADDR")
if addr != "" {
conf.Realms[0].KDC = []string{addr + ":88"}
}
// Apply the config to the client
cl.WithConfig(conf)
// Log in the client
err = cl.Login()
if err != nil {
panic(err)
}
// Form the request
url := "http://localhost" + port
r, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
// Apply the client's auth headers to the request
err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
if err != nil {
panic(err)
}
// Make the request
resp, err := http.DefaultClient.Do(r)
if err != nil {
panic(err)
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(b))
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/examples/httpServer.go 0000664 0000000 0000000 00000002612 13625372257 0025071 0 ustar 00root root 0000000 0000000 // +build examples
package main
import (
"encoding/hex"
"fmt"
"log"
"net/http"
"os"
//"github.com/pkg/profile"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/service"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
const (
port = ":9080"
)
func main() {
//defer profile.Start(profile.TraceProfile).Stop()
// Create logger
l := log.New(os.Stderr, "GOKRB5 Service: ", log.Ldate|log.Ltime|log.Lshortfile)
// Load the service's keytab
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
// Create the application's specific handler
th := http.HandlerFunc(testAppHandler)
// Set up handler mappings wrapping in the SPNEGOKRB5Authenticate handler wrapper
mux := http.NewServeMux()
mux.Handle("/", service.SPNEGOKRB5Authenticate(th, kt, "", false, l))
// Start up the web server
log.Fatal(http.ListenAndServe(port, mux))
}
// Simple application specific handler
func testAppHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
ctx := r.Context()
creds := ctx.Value(service.CTXKeyCredentials).(credentials.Credentials)
fmt.Fprintf(w,
`
GOKRB5 Handler
- Authenticed user: %s
- User's realm: %s
- Authn time: %v
- Session ID: %s
`,
creds.UserName(),
creds.Domain(),
creds.AuthTime(),
creds.SessionID(),
)
return
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gokrb5.go 0000664 0000000 0000000 00000000115 13625372257 0022272 0 ustar 00root root 0000000 0000000 // Package gokrb5 provides a Kerberos 5 implementation for Go
package gokrb5
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/ 0000775 0000000 0000000 00000000000 13625372257 0022043 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/ContextFlags.go 0000664 0000000 0000000 00000001216 13625372257 0024773 0 ustar 00root root 0000000 0000000 package gssapi
import "github.com/jcmturner/gofork/encoding/asn1"
/*
ContextFlags ::= BIT STRING {
delegFlag (0),
mutualFlag (1),
replayFlag (2),
sequenceFlag (3),
anonFlag (4),
confFlag (5),
integFlag (6)
} (SIZE (32))
*/
const (
delegFlag = 0
mutualFlag = 1
replayFlag = 2
sequenceFlag = 3
anonFlag = 4
confFlag = 5
integFlag = 6
)
// ContextFlags flags for GSSAPI
type ContextFlags asn1.BitString
// NewContextFlags creates a new ContextFlags instance.
func NewContextFlags() ContextFlags {
var c ContextFlags
c.BitLength = 32
c.Bytes = make([]byte, 4)
return c
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/MechType.go 0000664 0000000 0000000 00000000532 13625372257 0024110 0 ustar 00root root 0000000 0000000 package gssapi
import "github.com/jcmturner/gofork/encoding/asn1"
// MechTypeOIDKRB5 is the MechType OID for Kerberos 5
var MechTypeOIDKRB5 = asn1.ObjectIdentifier{1, 2, 840, 113554, 1, 2, 2}
// MechTypeOIDMSLegacyKRB5 is the MechType OID for MS legacy Kerberos 5
var MechTypeOIDMSLegacyKRB5 = asn1.ObjectIdentifier{1, 2, 840, 48018, 1, 2, 2}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/NegotiationToken.go 0000664 0000000 0000000 00000006553 13625372257 0025664 0 ustar 00root root 0000000 0000000 package gssapi
import (
"errors"
"fmt"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
/*
https://msdn.microsoft.com/en-us/library/ms995330.aspx
*/
// NegTokenInit implements Negotiation Token of type Init
type NegTokenInit struct {
MechTypes []asn1.ObjectIdentifier `asn1:"explicit,tag:0"`
ReqFlags ContextFlags `asn1:"explicit,optional,tag:1"`
MechToken []byte `asn1:"explicit,optional,tag:2"`
MechTokenMIC []byte `asn1:"explicit,optional,tag:3"`
}
// NegTokenResp implements Negotiation Token of type Resp/Targ
type NegTokenResp struct {
NegState asn1.Enumerated `asn1:"explicit,tag:0"`
SupportedMech asn1.ObjectIdentifier `asn1:"explicit,optional,tag:1"`
ResponseToken []byte `asn1:"explicit,optional,tag:2"`
MechListMIC []byte `asn1:"explicit,optional,tag:3"`
}
// NegTokenTarg implements Negotiation Token of type Resp/Targ
type NegTokenTarg NegTokenResp
// UnmarshalNegToken umarshals and returns either a NegTokenInit or a NegTokenResp.
//
// The boolean indicates if the response is a NegTokenInit.
// If error is nil and the boolean is false the response is a NegTokenResp.
func UnmarshalNegToken(b []byte) (bool, interface{}, error) {
var a asn1.RawValue
_, err := asn1.Unmarshal(b, &a)
if err != nil {
return false, nil, fmt.Errorf("error unmarshalling NegotiationToken: %v", err)
}
switch a.Tag {
case 0:
var negToken NegTokenInit
_, err = asn1.Unmarshal(a.Bytes, &negToken)
if err != nil {
return false, nil, fmt.Errorf("error unmarshalling NegotiationToken type %d (Init): %v", a.Tag, err)
}
return true, negToken, nil
case 1:
var negToken NegTokenResp
_, err = asn1.Unmarshal(a.Bytes, &negToken)
if err != nil {
return false, nil, fmt.Errorf("error unmarshalling NegotiationToken type %d (Resp/Targ): %v", a.Tag, err)
}
return false, negToken, nil
default:
return false, nil, errors.New("unknown choice type for NegotiationToken")
}
}
// Marshal an Init negotiation token
func (n *NegTokenInit) Marshal() ([]byte, error) {
b, err := asn1.Marshal(*n)
if err != nil {
return nil, err
}
nt := asn1.RawValue{
Tag: 0,
Class: 2,
IsCompound: true,
Bytes: b,
}
nb, err := asn1.Marshal(nt)
if err != nil {
return nil, err
}
return nb, nil
}
// Marshal a Resp/Targ negotiation token
func (n *NegTokenResp) Marshal() ([]byte, error) {
b, err := asn1.Marshal(*n)
if err != nil {
return nil, err
}
nt := asn1.RawValue{
Tag: 1,
Class: 2,
IsCompound: true,
Bytes: b,
}
nb, err := asn1.Marshal(nt)
if err != nil {
return nil, err
}
return nb, nil
}
// NewNegTokenInitKrb5 creates new Init negotiation token for Kerberos 5
func NewNegTokenInitKrb5(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey) (NegTokenInit, error) {
mt, err := NewAPREQMechToken(creds, tkt, sessionKey, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}, []int{})
if err != nil {
return NegTokenInit{}, fmt.Errorf("error getting MechToken; %v", err)
}
mtb, err := mt.Marshal()
if err != nil {
return NegTokenInit{}, fmt.Errorf("error marshalling MechToken; %v", err)
}
return NegTokenInit{
MechTypes: []asn1.ObjectIdentifier{MechTypeOIDKRB5},
MechToken: mtb,
}, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/NegotiationToken_test.go 0000664 0000000 0000000 00000010077 13625372257 0026717 0 ustar 00root root 0000000 0000000 package gssapi
import (
"encoding/hex"
"testing"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/stretchr/testify/assert"
)
const (
testNegTokenInit = "a08202aa308202a6a027302506092a864886f71201020206052b0501050206092a864882f71201020206062b0601050205a2820279048202756082027106092a864886f71201020201006e8202603082025ca003020105a10302010ea20703050000000000a38201706182016c30820168a003020105a10d1b0b544553542e474f4b524235a2233021a003020103a11a30181b04485454501b10686f73742e746573742e676f6b726235a382012b30820127a003020112a103020102a282011904820115d4bd890abc456f44e2e7a2e8111bd6767abf03266dfcda97c629af2ece450a5ae1f145e4a4d1bc2c848e66a6c6b31d9740b26b03cdbd2570bfcf126e90adf5f5ebce9e283ff5086da47b129b14fc0aabd4d1df9c1f3c72b80cc614dfc28783450b2c7b7749651f432b47aaa2ff158c0066b757f3fb00dd7b4f63d68276c76373ecdd3f19c66ebc43a81e577f3c263b878356f57e8d6c4eccd587b81538e70392cf7e73fc12a6f7c537a894a7bb5566c83ac4d69757aa320a51d8d690017aebf952add1889adfc3307b0e6cd8c9b57cf8589fbe52800acb6461c25473d49faa1bdceb8bce3f61db23f9cd6a09d5adceb411e1c4546b30b33331e570fd6bc50aa403557e75f488e759750ea038aab6454667d9b64f41a481d23081cfa003020112a281c70481c4d67ba2ae4cf5d917caab1d863605249320e90482563662ed92408a543b6ad5edeb8f9375e9060a205491df082fd2a5fec93dfb76f41012bb60cae20f07adbb77a1aa56f0521f36e1ea10dc9fb762902b254dd7664d0bcc6f751f2003e41990af1b4330d10477bfad638b9f0b704ac80cc47731f8ec8d801762bad8884b8de90adb1dbe7fc7b0ffafd38fb5eb8b6547cee30d89873281ce63ad70042a13478b1a7c2bdde0f223ace62dbb84e2d06f1070f4265f66e0544449335e2fcc4d0aee5bf81c5999"
testNegTokenResp = "a1143012a0030a0100a10b06092a864886f712010202"
)
func TestUnmarshal_negTokenInit(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testNegTokenInit)
if err != nil {
t.Fatalf("Error converting hex string test data to bytes: %v", err)
}
isInit, nt, err := UnmarshalNegToken(b)
if err != nil {
t.Fatalf("Error unmarshalling negotiation token: %v", err)
}
assert.IsType(t, NegTokenInit{}, nt, "Not the expected type NegTokenInit")
assert.True(t, isInit, "Boolean indicating type is negTokenInit is not true")
nInit := nt.(NegTokenInit)
assert.Equal(t, 4, len(nInit.MechTypes))
expectMechTypes := []asn1.ObjectIdentifier{
[]int{1, 2, 840, 113554, 1, 2, 2},
[]int{1, 3, 5, 1, 5, 2},
[]int{1, 2, 840, 48018, 1, 2, 2},
[]int{1, 3, 6, 1, 5, 2, 5},
}
assert.Equal(t, expectMechTypes, nInit.MechTypes, "MechTypes list in NegTokenInit not as expected")
}
func TestMarshal_negTokenInit(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testNegTokenInit)
if err != nil {
t.Fatalf("Error converting hex string test data to bytes: %v", err)
}
_, nt, err := UnmarshalNegToken(b)
if err != nil {
t.Fatalf("Error unmarshalling negotiation token: %v", err)
}
nInit := nt.(NegTokenInit)
mb, err := nInit.Marshal()
if err != nil {
t.Fatalf("Error marshalling negotiation init token: %v", err)
}
assert.Equal(t, b, mb, "Marshalled bytes not as expected for NegTokenInit")
}
func TestUnmarshal_negTokenResp(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testNegTokenResp)
if err != nil {
t.Fatalf("Error converting hex string test data to bytes: %v", err)
}
isInit, nt, err := UnmarshalNegToken(b)
if err != nil {
t.Fatalf("Error unmarshalling negotiation token: %v", err)
}
assert.IsType(t, NegTokenResp{}, nt, "Not the expected type NegTokenResp")
assert.False(t, isInit, "Boolean indicating type is negTokenInit is not false")
nResp := nt.(NegTokenResp)
assert.Equal(t, asn1.Enumerated(0), nResp.NegState)
assert.Equal(t, MechTypeOIDKRB5, nResp.SupportedMech, "SupportedMech type not as expected.")
}
func TestMarshal_negTokenResp(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testNegTokenResp)
if err != nil {
t.Fatalf("Error converting hex string test data to bytes: %v", err)
}
_, nt, err := UnmarshalNegToken(b)
if err != nil {
t.Fatalf("Error unmarshalling negotiation token: %v", err)
}
nResp := nt.(NegTokenResp)
mb, err := nResp.Marshal()
if err != nil {
t.Fatalf("Error marshalling negotiation init token: %v", err)
}
assert.Equal(t, b, mb, "Marshalled bytes not as expected for NegTokenResp")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/WrapToken.go 0000664 0000000 0000000 00000016764 13625372257 0024322 0 ustar 00root root 0000000 0000000 package gssapi
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
/*
From RFC 4121, section 4.2.6.2:
Quick notes:
- "EC" or "Extra Count" refers to the length of the cheksum.
- "Flags" (complete details in section 4.2.2) is a set of bits:
- if bit 0 is set, it means the token was sent by the acceptor (generally the kerberized service).
- bit 1 indicates that the token's payload is encrypted
- bit 2 indicates if the message is protected using a subkey defined by the acceptor.
- When computing checksums, EC and RRC MUST be set to 0.
- Wrap Tokens are not ASN.1 encoded.
*/
const (
HdrLen = 16 // Length of the Wrap Token's header
FillerByte byte = 0xFF
)
// WrapToken represents a GSS API Wrap token, as defined in RFC 4121.
// It contains the header fields, the payload and the checksum, and provides
// the logic for converting to/from bytes plus computing and verifying checksums
type WrapToken struct {
// const GSS Token ID: 0x0504
Flags byte // contains three flags: acceptor, sealed, acceptor subkey
// const Filler: 0xFF
EC uint16 // checksum length. big-endian
RRC uint16 // right rotation count. big-endian
SndSeqNum uint64 // sender's sequence number. big-endian
Payload []byte // your data! :)
CheckSum []byte // authenticated checksum of { payload | header }
}
// Return the 2 bytes identifying a GSS API Wrap token
func getGssWrapTokenId() *[2]byte {
return &[2]byte{0x05, 0x04}
}
// Marshal the WrapToken into a byte slice.
// The payload should have been set and the checksum computed, otherwise an error is returned.
func (wt *WrapToken) Marshal() ([]byte, error) {
if wt.CheckSum == nil {
return nil, errors.New("checksum has not been set")
}
if wt.Payload == nil {
return nil, errors.New("payload has not been set")
}
pldOffset := HdrLen // Offset of the payload in the token
chkSOffset := HdrLen + len(wt.Payload) // Offset of the checksum in the token
bytes := make([]byte, chkSOffset+int(wt.EC))
copy(bytes[0:], getGssWrapTokenId()[:])
bytes[2] = wt.Flags
bytes[3] = FillerByte
binary.BigEndian.PutUint16(bytes[4:6], wt.EC)
binary.BigEndian.PutUint16(bytes[6:8], wt.RRC)
binary.BigEndian.PutUint64(bytes[8:16], wt.SndSeqNum)
copy(bytes[pldOffset:], wt.Payload)
copy(bytes[chkSOffset:], wt.CheckSum)
return bytes, nil
}
// ComputeAndSetCheckSum uses the passed encryption key and key usage to compute the checksum over the payload and
// the header, and sets the CheckSum field of this WrapToken.
// If the payload has not been set or the checksum has already been set, an error is returned.
func (wt *WrapToken) ComputeAndSetCheckSum(key types.EncryptionKey, keyUsage uint32) error {
if wt.Payload == nil {
return errors.New("payload has not been set")
}
if wt.CheckSum != nil {
return errors.New("checksum has already been computed")
}
chkSum, cErr := wt.ComputeCheckSum(key, keyUsage)
if cErr != nil {
return cErr
}
wt.CheckSum = chkSum
return nil
}
// ComputeCheckSum computes and returns the checksum of this token, computed using the passed key and key usage.
// Conforms to RFC 4121 in that the checksum will be computed over { body | header },
// with the EC and RRC flags zeroed out.
// In the context of Kerberos Wrap tokens, mostly keyusage GSSAPI_ACCEPTOR_SEAL (=22)
// and GSSAPI_INITIATOR_SEAL (=24) will be used.
// Note: This will NOT update the struct's Checksum field.
func (wt *WrapToken) ComputeCheckSum(key types.EncryptionKey, keyUsage uint32) ([]byte, error) {
if wt.Payload == nil {
return nil, errors.New("cannot compute checksum with uninitialized payload")
}
// Build a slice containing { payload | header }
checksumMe := make([]byte, HdrLen+len(wt.Payload))
copy(checksumMe[0:], wt.Payload)
copy(checksumMe[len(wt.Payload):], getChecksumHeader(wt.Flags, wt.SndSeqNum))
encType, err := crypto.GetEtype(key.KeyType)
if err != nil {
return nil, err
}
return encType.GetChecksumHash(key.KeyValue, checksumMe, keyUsage)
}
// Build a header suitable for a checksum computation
func getChecksumHeader(flags byte, senderSeqNum uint64) []byte {
header := make([]byte, 16)
copy(header[0:], []byte{0x05, 0x04, flags, 0xFF, 0x00, 0x00, 0x00, 0x00})
binary.BigEndian.PutUint64(header[8:], senderSeqNum)
return header
}
// VerifyCheckSum computes the token's checksum with the provided key and usage,
// and compares it to the checksum present in the token.
// In case of any failure, (false, Err) is returned, with Err an explanatory error.
func (wt *WrapToken) VerifyCheckSum(key types.EncryptionKey, keyUsage uint32) (bool, error) {
computed, cErr := wt.ComputeCheckSum(key, keyUsage)
if cErr != nil {
return false, cErr
}
if !bytes.Equal(computed, wt.CheckSum) {
return false, fmt.Errorf(
"checksum mismatch. Computed: %s, Contained in token: %s",
hex.EncodeToString(computed), hex.EncodeToString(wt.CheckSum))
}
return true, nil
}
// Unmarshal bytes into the corresponding WrapToken.
// If expectFromAcceptor is true, we expect the token to have been emitted by the gss acceptor,
// and will check the according flag, returning an error if the token does not match the expectation.
func (wt *WrapToken) Unmarshal(b []byte, expectFromAcceptor bool) error {
// Check if we can read a whole header
if len(b) < 16 {
return errors.New("bytes shorter than header length")
}
// Is the Token ID correct?
if !bytes.Equal(getGssWrapTokenId()[:], b[0:2]) {
return fmt.Errorf("wrong Token ID. Expected %s, was %s",
hex.EncodeToString(getGssWrapTokenId()[:]),
hex.EncodeToString(b[0:2]))
}
// Check the acceptor flag
flags := b[2]
isFromAcceptor := flags&0x01 == 1
if isFromAcceptor && !expectFromAcceptor {
return errors.New("unexpected acceptor flag is set: not expecting a token from the acceptor")
}
if !isFromAcceptor && expectFromAcceptor {
return errors.New("expected acceptor flag is not set: expecting a token from the acceptor, not the initiator")
}
// Check the filler byte
if b[3] != FillerByte {
return fmt.Errorf("unexpected filler byte: expecting 0xFF, was %s ", hex.EncodeToString(b[3:4]))
}
checksumL := binary.BigEndian.Uint16(b[4:6])
// Sanity check on the checksum length
if int(checksumL) > len(b)-HdrLen {
return fmt.Errorf("inconsistent checksum length: %d bytes to parse, checksum length is %d", len(b), checksumL)
}
wt.Flags = flags
wt.EC = checksumL
wt.RRC = binary.BigEndian.Uint16(b[6:8])
wt.SndSeqNum = binary.BigEndian.Uint64(b[8:16])
wt.Payload = b[16 : len(b)-int(checksumL)]
wt.CheckSum = b[len(b)-int(checksumL):]
return nil
}
// NewInitiatorToken builds a new initiator token (acceptor flag will be set to 0) and computes the authenticated checksum.
// Other flags are set to 0, and the RRC and sequence number are initialized to 0.
// Note that in certain circumstances you may need to provide a sequence number that has been defined earlier.
// This is currently not supported.
func NewInitiatorToken(payload []byte, key types.EncryptionKey) (*WrapToken, error) {
encType, err := crypto.GetEtype(key.KeyType)
if err != nil {
return nil, err
}
token := WrapToken{
Flags: 0x00, // all zeroed out (this is a token sent by the initiator)
// Checksum size: lenth of output of the HMAC function, in bytes.
EC: uint16(encType.GetHMACBitLength() / 8),
RRC: 0,
SndSeqNum: 0,
Payload: payload,
}
if err := token.ComputeAndSetCheckSum(key, keyusage.GSSAPI_INITIATOR_SEAL); err != nil {
return nil, err
}
return &token, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/WrapToken_test.go 0000664 0000000 0000000 00000015355 13625372257 0025354 0 ustar 00root root 0000000 0000000 package gssapi
import (
"encoding/binary"
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
const (
// What a kerberized server might send
testChallengeFromAcceptor = "050401ff000c000000000000575e85d601010000853b728d5268525a1386c19f"
// What an initiator client could reply
testChallengeReplyFromInitiator = "050400ff000c000000000000000000000101000079a033510b6f127212242b97"
// session key used to sign the tokens above
sessionKey = "14f9bde6b50ec508201a97f74c4e5bd3"
sessionKeyType = 17
acceptorSeal = keyusage.GSSAPI_ACCEPTOR_SEAL
initiatorSeal = keyusage.GSSAPI_INITIATOR_SEAL
)
func getSessionKey() types.EncryptionKey {
key, _ := hex.DecodeString(sessionKey)
return types.EncryptionKey{
KeyType: sessionKeyType,
KeyValue: key,
}
}
func getChallengeReference() *WrapToken {
challenge, _ := hex.DecodeString(testChallengeFromAcceptor)
return &WrapToken{
Flags: 0x01,
EC: 12,
RRC: 0,
SndSeqNum: binary.BigEndian.Uint64(challenge[8:16]),
Payload: []byte{0x01, 0x01, 0x00, 0x00},
CheckSum: challenge[20:32],
}
}
func getChallengeReferenceNoChksum() *WrapToken {
c := getChallengeReference()
c.CheckSum = nil
return c
}
func getResponseReference() *WrapToken {
response, _ := hex.DecodeString(testChallengeReplyFromInitiator)
return &WrapToken{
Flags: 0x00,
EC: 12,
RRC: 0,
SndSeqNum: 0,
Payload: []byte{0x01, 0x01, 0x00, 0x00},
CheckSum: response[20:32],
}
}
func getResponseReferenceNoChkSum() *WrapToken {
r := getResponseReference()
r.CheckSum = nil
return r
}
func TestUnmarshal_Challenge(t *testing.T) {
t.Parallel()
challenge, _ := hex.DecodeString(testChallengeFromAcceptor)
var wt WrapToken
err := wt.Unmarshal(challenge, true)
assert.Nil(t, err, "Unexpected error occurred.")
assert.Equal(t, getChallengeReference(), &wt, "Token not decoded as expected.")
}
func TestUnmarshalFailure_Challenge(t *testing.T) {
t.Parallel()
challenge, _ := hex.DecodeString(testChallengeFromAcceptor)
var wt WrapToken
err := wt.Unmarshal(challenge, false)
assert.NotNil(t, err, "Expected error did not occur: a message from the acceptor cannot be expected to be sent from the initiator.")
assert.Nil(t, wt.Payload, "Token fields should not have been initialised")
assert.Nil(t, wt.CheckSum, "Token fields should not have been initialised")
assert.Equal(t, byte(0x00), wt.Flags, "Token fields should not have been initialised")
assert.Equal(t, uint16(0), wt.EC, "Token fields should not have been initialised")
assert.Equal(t, uint16(0), wt.RRC, "Token fields should not have been initialised")
assert.Equal(t, uint64(0), wt.SndSeqNum, "Token fields should not have been initialised")
}
func TestUnmarshal_ChallengeReply(t *testing.T) {
t.Parallel()
response, _ := hex.DecodeString(testChallengeReplyFromInitiator)
var wt WrapToken
err := wt.Unmarshal(response, false)
assert.Nil(t, err, "Unexpected error occurred.")
assert.Equal(t, getResponseReference(), &wt, "Token not decoded as expected.")
}
func TestUnmarshalFailure_ChallengeReply(t *testing.T) {
t.Parallel()
response, _ := hex.DecodeString(testChallengeReplyFromInitiator)
var wt WrapToken
err := wt.Unmarshal(response, true)
assert.NotNil(t, err, "Expected error did not occur: a message from the initiator cannot be expected to be sent from the acceptor.")
assert.Nil(t, wt.Payload, "Token fields should not have been initialised")
assert.Nil(t, wt.CheckSum, "Token fields should not have been initialised")
assert.Equal(t, byte(0x00), wt.Flags, "Token fields should not have been initialised")
assert.Equal(t, uint16(0), wt.EC, "Token fields should not have been initialised")
assert.Equal(t, uint16(0), wt.RRC, "Token fields should not have been initialised")
assert.Equal(t, uint64(0), wt.SndSeqNum, "Token fields should not have been initialised")
}
func TestChallengeChecksumVerification(t *testing.T) {
t.Parallel()
challenge, _ := hex.DecodeString(testChallengeFromAcceptor)
var wt WrapToken
wt.Unmarshal(challenge, true)
challengeOk, cErr := wt.VerifyCheckSum(getSessionKey(), acceptorSeal)
assert.Nil(t, cErr, "Error occurred during checksum verification.")
assert.True(t, challengeOk, "Checksum verification failed.")
}
func TestResponseChecksumVerification(t *testing.T) {
t.Parallel()
reply, _ := hex.DecodeString(testChallengeReplyFromInitiator)
var wt WrapToken
wt.Unmarshal(reply, false)
replyOk, rErr := wt.VerifyCheckSum(getSessionKey(), initiatorSeal)
assert.Nil(t, rErr, "Error occurred during checksum verification.")
assert.True(t, replyOk, "Checksum verification failed.")
}
func TestChecksumVerificationFailure(t *testing.T) {
t.Parallel()
challenge, _ := hex.DecodeString(testChallengeFromAcceptor)
var wt WrapToken
wt.Unmarshal(challenge, true)
// Test a failure with the correct key but wrong keyusage:
challengeOk, cErr := wt.VerifyCheckSum(getSessionKey(), initiatorSeal)
assert.NotNil(t, cErr, "Expected error did not occur.")
assert.False(t, challengeOk, "Checksum verification succeeded when it should have failed.")
wrongKeyVal, _ := hex.DecodeString("14f9bde6b50ec508201a97f74c4effff")
badKey := types.EncryptionKey{
KeyType: sessionKeyType,
KeyValue: wrongKeyVal,
}
// Test a failure with the wrong key but correct keyusage:
wrongKeyOk, wkErr := wt.VerifyCheckSum(badKey, acceptorSeal)
assert.NotNil(t, wkErr, "Expected error did not occur.")
assert.False(t, wrongKeyOk, "Checksum verification succeeded when it should have failed.")
}
func TestMarshal_Challenge(t *testing.T) {
t.Parallel()
bytes, _ := getChallengeReference().Marshal()
assert.Equal(t, testChallengeFromAcceptor, hex.EncodeToString(bytes),
"Marshalling did not yield the expected result.")
}
func TestMarshal_ChallengeReply(t *testing.T) {
t.Parallel()
bytes, _ := getResponseReference().Marshal()
assert.Equal(t, testChallengeReplyFromInitiator, hex.EncodeToString(bytes),
"Marshalling did not yield the expected result.")
}
func TestMarshal_Failures(t *testing.T) {
t.Parallel()
noChkSum := getResponseReferenceNoChkSum()
chkBytes, chkErr := noChkSum.Marshal()
assert.Nil(t, chkBytes, "No bytes should be returned.")
assert.NotNil(t, chkErr, "Expected an error as no checksum was set")
noPayload := getResponseReference()
noPayload.Payload = nil
pldBytes, pldErr := noPayload.Marshal()
assert.Nil(t, pldBytes, "No bytes should be returned.")
assert.NotNil(t, pldErr, "Expected an error as no checksum was set")
}
func TestNewInitiatorTokenSignatureAndMarshalling(t *testing.T) {
t.Parallel()
token, tErr := NewInitiatorToken([]byte{0x01, 0x01, 0x00, 0x00}, getSessionKey())
assert.Nil(t, tErr, "Unexepected error.")
assert.Equal(t, getResponseReference(), token, "Token failed to be marshalled to the expected bytes.")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/gssapi.go 0000664 0000000 0000000 00000005573 13625372257 0023672 0 ustar 00root root 0000000 0000000 // Package gssapi implements Generic Security Services Application Program Interface required for SPNEGO kerberos authentication.
package gssapi
import (
"errors"
"fmt"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/asn1tools"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// SPNEGO_OID is the OID for SPNEGO header type.
var SPNEGO_OID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 2}
// SPNEGO header struct
type SPNEGO struct {
Init bool
Resp bool
NegTokenInit NegTokenInit
NegTokenResp NegTokenResp
}
// Unmarshal SPNEGO negotiation token
func (s *SPNEGO) Unmarshal(b []byte) error {
var r []byte
var err error
if b[0] != byte(161) {
// Not a NegTokenResp/Targ could be a NegTokenInit
var oid asn1.ObjectIdentifier
r, err = asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0))
if err != nil {
return fmt.Errorf("not a valid SPNEGO token: %v", err)
}
// Check the OID is the SPNEGO OID value
if !oid.Equal(SPNEGO_OID) {
return fmt.Errorf("OID %s does not match SPNEGO OID %s", oid.String(), SPNEGO_OID.String())
}
} else {
// Could be a NegTokenResp/Targ
r = b
}
var a asn1.RawValue
_, err = asn1.Unmarshal(r, &a)
if err != nil {
return fmt.Errorf("error unmarshalling SPNEGO: %v", err)
}
switch a.Tag {
case 0:
_, err = asn1.Unmarshal(a.Bytes, &s.NegTokenInit)
if err != nil {
return fmt.Errorf("error unmarshalling NegotiationToken type %d (Init): %v", a.Tag, err)
}
s.Init = true
case 1:
_, err = asn1.Unmarshal(a.Bytes, &s.NegTokenResp)
if err != nil {
return fmt.Errorf("error unmarshalling NegotiationToken type %d (Resp/Targ): %v", a.Tag, err)
}
s.Resp = true
default:
return errors.New("unknown choice type for NegotiationToken")
}
return nil
}
// Marshal SPNEGO negotiation token
func (s *SPNEGO) Marshal() ([]byte, error) {
var b []byte
if s.Init {
hb, _ := asn1.Marshal(SPNEGO_OID)
tb, err := s.NegTokenInit.Marshal()
if err != nil {
return b, fmt.Errorf("could not marshal NegTokenInit: %v", err)
}
b = append(hb, tb...)
return asn1tools.AddASNAppTag(b, 0), nil
}
if s.Resp {
b, err := s.NegTokenResp.Marshal()
if err != nil {
return b, fmt.Errorf("could not marshal NegTokenResp: %v", err)
}
return b, nil
}
return b, errors.New("SPNEGO cannot be marshalled. It contains neither a NegTokenInit or NegTokenResp")
}
// GetSPNEGOKrbNegTokenInit returns an SPNEGO struct containing a NegTokenInit.
func GetSPNEGOKrbNegTokenInit(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey) (SPNEGO, error) {
negTokenInit, err := NewNegTokenInitKrb5(creds, tkt, sessionKey)
if err != nil {
return SPNEGO{}, fmt.Errorf("could not create NegTokenInit: %v", err)
}
return SPNEGO{
Init: true,
NegTokenInit: negTokenInit,
}, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/gssapi_test.go 0000664 0000000 0000000 00000010223 13625372257 0024715 0 ustar 00root root 0000000 0000000 package gssapi
import (
"encoding/hex"
"testing"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/stretchr/testify/assert"
)
const (
testGSSAPIInit = "608202b606062b0601050502a08202aa308202a6a027302506092a864886f71201020206052b0501050206092a864882f71201020206062b0601050205a2820279048202756082027106092a864886f71201020201006e8202603082025ca003020105a10302010ea20703050000000000a38201706182016c30820168a003020105a10d1b0b544553542e474f4b524235a2233021a003020103a11a30181b04485454501b10686f73742e746573742e676f6b726235a382012b30820127a003020112a103020102a282011904820115d4bd890abc456f44e2e7a2e8111bd6767abf03266dfcda97c629af2ece450a5ae1f145e4a4d1bc2c848e66a6c6b31d9740b26b03cdbd2570bfcf126e90adf5f5ebce9e283ff5086da47b129b14fc0aabd4d1df9c1f3c72b80cc614dfc28783450b2c7b7749651f432b47aaa2ff158c0066b757f3fb00dd7b4f63d68276c76373ecdd3f19c66ebc43a81e577f3c263b878356f57e8d6c4eccd587b81538e70392cf7e73fc12a6f7c537a894a7bb5566c83ac4d69757aa320a51d8d690017aebf952add1889adfc3307b0e6cd8c9b57cf8589fbe52800acb6461c25473d49faa1bdceb8bce3f61db23f9cd6a09d5adceb411e1c4546b30b33331e570fd6bc50aa403557e75f488e759750ea038aab6454667d9b64f41a481d23081cfa003020112a281c70481c4eb593beb5afcb1a2a669d54cb85a3772231559f2d40c9f8f053f218ba6eb084ed7efc467d94b88bcd189dda920d6e675ec001a6a2bca11f0a1de37f2f7ae9929f94a86d625b2ec1b213a88cbae6099dda7b172cd3bd1802cb177ae4554d59277004bfd3435248f55044fe7af7b2c9c5a3c43763278c585395aebe2856cdff9f2569d8b823564ce6be2d19748b910ec06bd3c0a9bc5de51ddcf7d875f1108ca6ad935f52d90cb62a18197d9b8e796bef0fbe1463f61df61cfbce6008ae9e1a2d2314a986d"
testGSSAPIResp = "a1143012a0030a0100a10b06092a864886f712010202"
)
func TestUnmarshal_SPNEGO_Init(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testGSSAPIInit)
if err != nil {
t.Fatalf("Error converting hex string test data to bytes: %v", err)
}
var s SPNEGO
err = s.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshalling SPNEGO with NegTokenInit: %v", err)
}
assert.True(t, s.Init, "SPNEGO does not indicate it contains NegTokenInit as expected")
assert.False(t, s.Resp, "SPNEGO indicates is contains a NegTokenResp but it shouldn't")
assert.Equal(t, 4, len(s.NegTokenInit.MechTypes))
expectMechTypes := []asn1.ObjectIdentifier{
MechTypeOIDKRB5,
[]int{1, 3, 5, 1, 5, 2},
MechTypeOIDMSLegacyKRB5,
[]int{1, 3, 6, 1, 5, 2, 5},
}
assert.Equal(t, expectMechTypes, s.NegTokenInit.MechTypes, "MechTypes list in NegTokenInit not as expected")
assert.NotZero(t, len(s.NegTokenInit.MechToken), "MechToken is zero in length")
}
func TestUnmarshal_SPNEGO_RespTarg(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testGSSAPIResp)
if err != nil {
t.Fatalf("Error converting hex string test data to bytes: %v", err)
}
var s SPNEGO
err = s.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshalling SPNEGO with NegTokenResp/NegTokenTarg: %v", err)
}
assert.True(t, s.Resp, "SPNEGO does not indicate it contains NegTokenResp/Targ as expected")
assert.False(t, s.Init, "SPNEGO indicates is contains a NegTokenInit but it shouldn't")
assert.Equal(t, asn1.Enumerated(0), s.NegTokenResp.NegState, "Negotiation state not as expected.")
assert.Equal(t, MechTypeOIDKRB5, s.NegTokenResp.SupportedMech, "SupportedMech type not as expected.")
}
func TestMarshal_SPNEGO_Init(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testGSSAPIInit)
if err != nil {
t.Fatalf("Error converting hex string test data to bytes: %v", err)
}
var s SPNEGO
err = s.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshalling SPNEGO with NegTokenInit: %v", err)
}
mb, err := s.Marshal()
if err != nil {
t.Fatalf("Error marshalling SPNEGO containing NegTokenInit: %v", err)
}
assert.Equal(t, b, mb, "Marshaled bytes not as expected")
}
func TestMarshal_SPNEGO_RespTarg(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testGSSAPIResp)
if err != nil {
t.Fatalf("Error converting hex string test data to bytes: %v", err)
}
var s SPNEGO
err = s.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshalling SPNEGO with NegTokenResp: %v", err)
}
mb, err := s.Marshal()
if err != nil {
t.Fatalf("Error marshalling SPNEGO containing NegTokenResp: %v", err)
}
assert.Equal(t, b, mb, "Marshaled bytes not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/krb5Token.go 0000664 0000000 0000000 00000011121 13625372257 0024232 0 ustar 00root root 0000000 0000000 package gssapi
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/asn1tools"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// GSSAPI MechToken IDs and flags.
const (
TOK_ID_KRB_AP_REQ = "0100"
TOK_ID_KRB_AP_REP = "0200"
TOK_ID_KRB_ERROR = "0300"
GSS_C_DELEG_FLAG = 1
GSS_C_MUTUAL_FLAG = 2
GSS_C_REPLAY_FLAG = 4
GSS_C_SEQUENCE_FLAG = 8
GSS_C_CONF_FLAG = 16
GSS_C_INTEG_FLAG = 32
)
// MechToken implementation for GSSAPI.
type MechToken struct {
OID asn1.ObjectIdentifier
TokID []byte
APReq messages.APReq
APRep messages.APRep
KRBError messages.KRBError
}
// Marshal a MechToken into a slice of bytes.
func (m *MechToken) Marshal() ([]byte, error) {
// Create the header
b, _ := asn1.Marshal(m.OID)
b = append(b, m.TokID...)
var tb []byte
var err error
switch hex.EncodeToString(m.TokID) {
case TOK_ID_KRB_AP_REQ:
tb, err = m.APReq.Marshal()
if err != nil {
return []byte{}, fmt.Errorf("error marshalling AP_REQ for MechToken: %v", err)
}
case TOK_ID_KRB_AP_REP:
return []byte{}, errors.New("marshal of AP_REP GSSAPI MechToken not supported by gokrb5")
case TOK_ID_KRB_ERROR:
return []byte{}, errors.New("marshal of KRB_ERROR GSSAPI MechToken not supported by gokrb5")
}
if err != nil {
return []byte{}, fmt.Errorf("error mashalling kerberos message within mech token: %v", err)
}
b = append(b, tb...)
return asn1tools.AddASNAppTag(b, 0), nil
}
// Unmarshal a MechToken.
func (m *MechToken) Unmarshal(b []byte) error {
var oid asn1.ObjectIdentifier
r, err := asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0))
if err != nil {
return fmt.Errorf("error unmarshalling MechToken OID: %v", err)
}
m.OID = oid
m.TokID = r[0:2]
switch hex.EncodeToString(m.TokID) {
case TOK_ID_KRB_AP_REQ:
var a messages.APReq
err = a.Unmarshal(r[2:])
if err != nil {
return fmt.Errorf("error unmarshalling MechToken AP_REQ: %v", err)
}
m.APReq = a
case TOK_ID_KRB_AP_REP:
var a messages.APRep
err = a.Unmarshal(r[2:])
if err != nil {
return fmt.Errorf("error unmarshalling MechToken AP_REP: %v", err)
}
m.APRep = a
case TOK_ID_KRB_ERROR:
var a messages.KRBError
err = a.Unmarshal(r[2:])
if err != nil {
return fmt.Errorf("error unmarshalling MechToken KRBError: %v", err)
}
m.KRBError = a
}
return nil
}
// IsAPReq tests if the MechToken contains an AP_REQ.
func (m *MechToken) IsAPReq() bool {
if hex.EncodeToString(m.TokID) == TOK_ID_KRB_AP_REQ {
return true
}
return false
}
// IsAPRep tests if the MechToken contains an AP_REP.
func (m *MechToken) IsAPRep() bool {
if hex.EncodeToString(m.TokID) == TOK_ID_KRB_AP_REP {
return true
}
return false
}
// IsKRBError tests if the MechToken contains an KRB_ERROR.
func (m *MechToken) IsKRBError() bool {
if hex.EncodeToString(m.TokID) == TOK_ID_KRB_ERROR {
return true
}
return false
}
// NewAPREQMechToken creates new Kerberos AP_REQ MechToken.
func NewAPREQMechToken(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey, GSSAPIFlags []int, APOptions []int) (MechToken, error) {
var m MechToken
m.OID = MechTypeOIDKRB5
tb, _ := hex.DecodeString(TOK_ID_KRB_AP_REQ)
m.TokID = tb
auth, err := NewAuthenticator(creds, GSSAPIFlags)
if err != nil {
return m, err
}
APReq, err := messages.NewAPReq(
tkt,
sessionKey,
auth,
)
if err != nil {
return m, err
}
for _, o := range APOptions {
types.SetFlag(&APReq.APOptions, o)
}
m.APReq = APReq
return m, nil
}
// NewAuthenticator creates a new kerberos authenticator for kerberos MechToken
func NewAuthenticator(creds credentials.Credentials, flags []int) (types.Authenticator, error) {
//RFC 4121 Section 4.1.1
auth, err := types.NewAuthenticator(creds.Realm, creds.CName)
if err != nil {
return auth, krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
}
auth.Cksum = types.Checksum{
CksumType: chksumtype.GSSAPI,
Checksum: newAuthenticatorChksum(flags),
}
return auth, nil
}
// Create new authenticator checksum for kerberos MechToken
func newAuthenticatorChksum(flags []int) []byte {
a := make([]byte, 24)
binary.LittleEndian.PutUint32(a[:4], 16)
for _, i := range flags {
if i == GSS_C_DELEG_FLAG {
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
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/krb5Token_test.go 0000664 0000000 0000000 00000014560 13625372257 0025303 0 ustar 00root root 0000000 0000000 package gssapi
import (
"encoding/hex"
"math"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
const (
MechTokenHex = "6082026306092a864886f71201020201006e8202523082024ea003020105a10302010ea20703050000000000a382015d6182015930820155a003020105a10d1b0b544553542e474f4b524235a2233021a003020101a11a30181b04485454501b10686f73742e746573742e676f6b726235a382011830820114a003020112a103020103a28201060482010230621d868c97f30bf401e03bbffcd724bd9d067dce2afc31f71a356449b070cdafcc1ff372d0eb1e7a708b50c0152f3996c45b1ea312a803907fb97192d39f20cdcaea29876190f51de6e2b4a4df0460122ed97f363434e1e120b0e76c172b4424a536987152ac0b73013ab88af4b13a3fcdc63f739039dd46d839709cf5b51bb0ce6cb3af05fab3844caac280929955495235e9d0424f8a1fb9b4bd4f6bba971f40b97e9da60b9dabfcf0b1feebfca02c9a19b327a0004aa8e19192726cf347561fa8ac74afad5d6a264e50cf495b93aac86c77b2bc2d184234f6c2767dbea431485a25687b9044a20b601e968efaefffa1fc5283ff32aa6a53cb6c5cdd2eddcb26a481d73081d4a003020112a103020103a281c70481c4a1b29e420324f7edf9efae39df7bcaaf196a3160cf07e72f52a4ef8a965721b2f3343719c50699046e4fcc18ca26c2bfc7e4a9eddfc9d9cfc57ff2f6bdbbd1fc40ac442195bc669b9a0dbba12563b3e4cac9f4022fc01b8aa2d1ab84815bb078399ff7f4d5f9815eef896a0c7e3c049e6fd9932b97096cdb5861425b9d81753d0743212ded1a0fb55a00bf71a46be5ce5e1c8a5cc327b914347d9efcb6cb31ca363b1850d95c7b6c4c3cc6301615ad907318a0c5379d343610fab17eca9c7dc0a5a60658"
AuthChksum = "100000000000000000000000000000000000000030000000"
)
func TestMechToken_Unmarshal(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(MechTokenHex)
if err != nil {
t.Fatalf("Error decoding MechToken hex: %v", err)
}
var mt MechToken
err = mt.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshalling MechToken: %v", err)
}
assert.Equal(t, MechTypeOIDKRB5, mt.OID, "MechToken OID not as expected.")
assert.Equal(t, []byte{1, 0}, mt.TokID, "TokID not as expected")
assert.Equal(t, msgtype.KRB_AP_REQ, mt.APReq.MsgType, "MechToken AP_REQ does not have the right message type.")
assert.Equal(t, int32(0), mt.KRBError.ErrorCode, "KRBError in MechToken does not indicate no error.")
assert.Equal(t, int32(18), mt.APReq.Authenticator.EType, "Authenticator within AP_REQ does not have the etype expected.")
}
func TestMechToken_newAuthenticatorChksum(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(AuthChksum)
if err != nil {
t.Fatalf("Error decoding MechToken hex: %v", err)
}
cb := newAuthenticatorChksum([]int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG})
assert.Equal(t, b, cb, "SPNEGO Authenticator checksum not as expected")
}
// Test with explicit subkey generation.
func TestMechToken_newAuthenticatorWithSubkeyGeneration(t *testing.T) {
t.Parallel()
creds := credentials.NewCredentials("hftsai", testdata.TEST_REALM)
creds.CName.NameString = testdata.TEST_PRINCIPALNAME_NAMESTRING
var etypeID int32 = 18
keyLen := 32 // etypeID 18 refers to AES256 -> 32 bytes key
a, err := NewAuthenticator(creds, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG})
if err != nil {
t.Fatalf("Error creating authenticator: %v", err)
}
a.GenerateSeqNumberAndSubKey(etypeID, keyLen)
assert.Equal(t, int32(32771), a.Cksum.CksumType, "Checksum type in authenticator for SPNEGO mechtoken not as expected.")
assert.Equal(t, etypeID, a.SubKey.KeyType, "Subkey not of the expected type.")
assert.Equal(t, keyLen, len(a.SubKey.KeyValue), "Subkey value not of the right length")
var nz bool
for _, b := range a.SubKey.KeyValue {
if b != byte(0) {
nz = true
}
}
assert.True(t, nz, "subkey not initialised")
assert.Condition(t, assert.Comparison(func() bool {
return a.SeqNumber > 0
}), "Sequence number is not greater than zero")
assert.Condition(t, assert.Comparison(func() bool {
return a.SeqNumber <= math.MaxUint32
}))
}
// Test without subkey generation.
func TestMechToken_newAuthenticator(t *testing.T) {
t.Parallel()
creds := credentials.NewCredentials("hftsai", testdata.TEST_REALM)
creds.CName.NameString = testdata.TEST_PRINCIPALNAME_NAMESTRING
a, err := NewAuthenticator(creds, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG})
if err != nil {
t.Fatalf("Error creating authenticator: %v", err)
}
assert.Equal(t, int32(32771), a.Cksum.CksumType, "Checksum type in authenticator for SPNEGO mechtoken not as expected.")
assert.Equal(t, int32(0), a.SubKey.KeyType, "Subkey not of the expected type.")
assert.Nil(t, a.SubKey.KeyValue, "Subkey should not be set.")
assert.Condition(t, assert.Comparison(func() bool {
return a.SeqNumber > 0
}), "Sequence number is not greater than zero")
assert.Condition(t, assert.Comparison(func() bool {
return a.SeqNumber <= math.MaxUint32
}))
}
func TestNewAPREQMechToken_and_Marshal(t *testing.T) {
t.Parallel()
creds := credentials.NewCredentials("hftsai", testdata.TEST_REALM)
creds.CName.NameString = testdata.TEST_PRINCIPALNAME_NAMESTRING
var tkt messages.Ticket
v := "encode_krb5_ticket"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = tkt.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
key := types.EncryptionKey{
KeyType: 18,
KeyValue: make([]byte, 32),
}
mt, err := NewAPREQMechToken(creds, tkt, key, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}, []int{})
if err != nil {
t.Fatalf("Error creating MechToken: %v", err)
}
mb, err := mt.Marshal()
if err != nil {
t.Fatalf("Error unmarshalling MechToken: %v", err)
}
err = mt.Unmarshal(mb)
if err != nil {
t.Fatalf("Error unmarshalling MechToken: %v", err)
}
assert.Equal(t, MechTypeOIDKRB5, mt.OID, "MechToken OID not as expected.")
assert.Equal(t, []byte{1, 0}, mt.TokID, "TokID not as expected")
assert.Equal(t, msgtype.KRB_AP_REQ, mt.APReq.MsgType, "MechToken AP_REQ does not have the right message type.")
assert.Equal(t, int32(0), mt.KRBError.ErrorCode, "KRBError in MechToken does not indicate no error.")
assert.Equal(t, testdata.TEST_REALM, mt.APReq.Ticket.Realm, "Realm in ticket within the AP_REQ of the MechToken not as expected.")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, mt.APReq.Ticket.SName.NameString, "SName in ticket within the AP_REQ of the MechToken not as expected.")
assert.Equal(t, int32(18), mt.APReq.Authenticator.EType, "Authenticator within AP_REQ does not have the etype expected.")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/ 0000775 0000000 0000000 00000000000 13625372257 0021465 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/addrtype/ 0000775 0000000 0000000 00000000000 13625372257 0023301 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/addrtype/constants.go 0000664 0000000 0000000 00000000522 13625372257 0025643 0 ustar 00root root 0000000 0000000 // Package addrtype provides Address type assigned numbers.
package addrtype
// Address type IDs.
const (
IPv4 int32 = 2
Directional int32 = 3
ChaosNet int32 = 5
XNS int32 = 6
ISO int32 = 7
DECNETPhaseIV int32 = 12
AppleTalkDDP int32 = 16
NetBios int32 = 20
IPv6 int32 = 24
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/adtype/ 0000775 0000000 0000000 00000000000 13625372257 0022753 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/adtype/constants.go 0000664 0000000 0000000 00000001471 13625372257 0025321 0 ustar 00root root 0000000 0000000 // Package adtype provides Authenticator type assigned numbers.
package adtype
// Authenticator type IDs.
const (
ADIfRelevant int32 = 1
ADIntendedForServer int32 = 2
ADIntendedForApplicationClass int32 = 3
ADKDCIssued int32 = 4
ADAndOr int32 = 5
ADMandatoryTicketExtensions int32 = 6
ADInTicketExtensions int32 = 7
ADMandatoryForKDC int32 = 8
OSFDCE int32 = 64
SESAME int32 = 65
ADOSFDCEPKICertID int32 = 66
ADAuthenticationStrength int32 = 70
ADFXFastArmor int32 = 71
ADFXFastUsed int32 = 72
ADWin2KPAC int32 = 128
ADEtypeNegotiation int32 = 129
//Reserved values 9-63
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/asnAppTag/ 0000775 0000000 0000000 00000000000 13625372257 0023343 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/asnAppTag/constants.go 0000664 0000000 0000000 00000000761 13625372257 0025712 0 ustar 00root root 0000000 0000000 // Package asnAppTag provides ASN1 application tag numbers.
package asnAppTag
// ASN1 application tag numbers.
const (
Ticket = 1
Authenticator = 2
EncTicketPart = 3
ASREQ = 10
TGSREQ = 12
ASREP = 11
TGSREP = 13
APREQ = 14
APREP = 15
KRBSafe = 20
KRBPriv = 21
KRBCred = 22
EncASRepPart = 25
EncTGSRepPart = 26
EncAPRepPart = 27
EncKrbPrivPart = 28
EncKrbCredPart = 29
KRBError = 30
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/chksumtype/ 0000775 0000000 0000000 00000000000 13625372257 0023661 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/chksumtype/constants.go 0000664 0000000 0000000 00000001743 13625372257 0026231 0 ustar 00root root 0000000 0000000 // Package chksumtype provides Kerberos 5 checksum type assigned numbers.
package chksumtype
// Checksum type IDs.
const (
//RESERVED : 0
CRC32 int32 = 1
RSA_MD4 int32 = 2
RSA_MD4_DES int32 = 3
DES_MAC int32 = 4
DES_MAC_K int32 = 5
RSA_MD4_DES_K int32 = 6
RSA_MD5 int32 = 7
RSA_MD5_DES int32 = 8
RSA_MD5_DES3 int32 = 9
SHA1_ID10 int32 = 10
//UNASSIGNED : 11
HMAC_SHA1_DES3_KD int32 = 12
HMAC_SHA1_DES3 int32 = 13
SHA1_ID14 int32 = 14
HMAC_SHA1_96_AES128 int32 = 15
HMAC_SHA1_96_AES256 int32 = 16
CMAC_CAMELLIA128 int32 = 17
CMAC_CAMELLIA256 int32 = 18
HMAC_SHA256_128_AES128 int32 = 19
HMAC_SHA384_192_AES256 int32 = 20
//UNASSIGNED : 21-32770
GSSAPI int32 = 32771
//UNASSIGNED : 32772-2147483647
KERB_CHECKSUM_HMAC_MD5_UNSIGNED uint32 = 4294967158 // 0xFFFFFF76 documentation says this is -138 but in an unsigned int this is 4294967158
KERB_CHECKSUM_HMAC_MD5 int32 = -138
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/constants.go 0000664 0000000 0000000 00000000173 13625372257 0024031 0 ustar 00root root 0000000 0000000 // Package iana provides Kerberos 5 assigned numbers.
package iana
// PVNO is the Protocol Version Number.
const PVNO = 5
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/errorcode/ 0000775 0000000 0000000 00000000000 13625372257 0023451 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/errorcode/constants.go 0000664 0000000 0000000 00000030564 13625372257 0026024 0 ustar 00root root 0000000 0000000 // Package errorcode provides Kerberos 5 assigned error codes.
package errorcode
import "fmt"
// Kerberos error codes.
const (
KDC_ERR_NONE int32 = 0 //No error
KDC_ERR_NAME_EXP int32 = 1 //Client's entry in database has expired
KDC_ERR_SERVICE_EXP int32 = 2 //Server's entry in database has expired
KDC_ERR_BAD_PVNO int32 = 3 //Requested protocol version number not supported
KDC_ERR_C_OLD_MAST_KVNO int32 = 4 //Client's key encrypted in old master key
KDC_ERR_S_OLD_MAST_KVNO int32 = 5 //Server's key encrypted in old master key
KDC_ERR_C_PRINCIPAL_UNKNOWN int32 = 6 //Client not found in Kerberos database
KDC_ERR_S_PRINCIPAL_UNKNOWN int32 = 7 //Server not found in Kerberos database
KDC_ERR_PRINCIPAL_NOT_UNIQUE int32 = 8 //Multiple principal entries in database
KDC_ERR_NULL_KEY int32 = 9 //The client or server has a null key
KDC_ERR_CANNOT_POSTDATE int32 = 10 //Ticket not eligible for postdating
KDC_ERR_NEVER_VALID int32 = 11 //Requested starttime is later than end time
KDC_ERR_POLICY int32 = 12 //KDC policy rejects request
KDC_ERR_BADOPTION int32 = 13 //KDC cannot accommodate requested option
KDC_ERR_ETYPE_NOSUPP int32 = 14 //KDC has no support for encryption type
KDC_ERR_SUMTYPE_NOSUPP int32 = 15 //KDC has no support for checksum type
KDC_ERR_PADATA_TYPE_NOSUPP int32 = 16 //KDC has no support for padata type
KDC_ERR_TRTYPE_NOSUPP int32 = 17 //KDC has no support for transited type
KDC_ERR_CLIENT_REVOKED int32 = 18 //Clients credentials have been revoked
KDC_ERR_SERVICE_REVOKED int32 = 19 //Credentials for server have been revoked
KDC_ERR_TGT_REVOKED int32 = 20 //TGT has been revoked
KDC_ERR_CLIENT_NOTYET int32 = 21 //Client not yet valid; try again later
KDC_ERR_SERVICE_NOTYET int32 = 22 //Server not yet valid; try again later
KDC_ERR_KEY_EXPIRED int32 = 23 //Password has expired; change password to reset
KDC_ERR_PREAUTH_FAILED int32 = 24 //Pre-authentication information was invalid
KDC_ERR_PREAUTH_REQUIRED int32 = 25 //Additional pre-authentication required
KDC_ERR_SERVER_NOMATCH int32 = 26 //Requested server and ticket don't match
KDC_ERR_MUST_USE_USER2USER int32 = 27 //Server principal valid for user2user only
KDC_ERR_PATH_NOT_ACCEPTED int32 = 28 //KDC Policy rejects transited path
KDC_ERR_SVC_UNAVAILABLE int32 = 29 //A service is not available
KRB_AP_ERR_BAD_INTEGRITY int32 = 31 //Integrity check on decrypted field failed
KRB_AP_ERR_TKT_EXPIRED int32 = 32 //Ticket expired
KRB_AP_ERR_TKT_NYV int32 = 33 //Ticket not yet valid
KRB_AP_ERR_REPEAT int32 = 34 //Request is a replay
KRB_AP_ERR_NOT_US int32 = 35 //The ticket isn't for us
KRB_AP_ERR_BADMATCH int32 = 36 //Ticket and authenticator don't match
KRB_AP_ERR_SKEW int32 = 37 //Clock skew too great
KRB_AP_ERR_BADADDR int32 = 38 //Incorrect net address
KRB_AP_ERR_BADVERSION int32 = 39 //Protocol version mismatch
KRB_AP_ERR_MSG_TYPE int32 = 40 //Invalid msg type
KRB_AP_ERR_MODIFIED int32 = 41 //Message stream modified
KRB_AP_ERR_BADORDER int32 = 42 //Message out of order
KRB_AP_ERR_BADKEYVER int32 = 44 //Specified version of key is not available
KRB_AP_ERR_NOKEY int32 = 45 //Service key not available
KRB_AP_ERR_MUT_FAIL int32 = 46 //Mutual authentication failed
KRB_AP_ERR_BADDIRECTION int32 = 47 //Incorrect message direction
KRB_AP_ERR_METHOD int32 = 48 //Alternative authentication method required
KRB_AP_ERR_BADSEQ int32 = 49 //Incorrect sequence number in message
KRB_AP_ERR_INAPP_CKSUM int32 = 50 //Inappropriate type of checksum in message
KRB_AP_PATH_NOT_ACCEPTED int32 = 51 //Policy rejects transited path
KRB_ERR_RESPONSE_TOO_BIG int32 = 52 //Response too big for UDP; retry with TCP
KRB_ERR_GENERIC int32 = 60 //Generic error (description in e-text)
KRB_ERR_FIELD_TOOLONG int32 = 61 //Field is too long for this implementation
KDC_ERROR_CLIENT_NOT_TRUSTED int32 = 62 //Reserved for PKINIT
KDC_ERROR_KDC_NOT_TRUSTED int32 = 63 //Reserved for PKINIT
KDC_ERROR_INVALID_SIG int32 = 64 //Reserved for PKINIT
KDC_ERR_KEY_TOO_WEAK int32 = 65 //Reserved for PKINIT
KDC_ERR_CERTIFICATE_MISMATCH int32 = 66 //Reserved for PKINIT
KRB_AP_ERR_NO_TGT int32 = 67 //No TGT available to validate USER-TO-USER
KDC_ERR_WRONG_REALM int32 = 68 //Reserved for future use
KRB_AP_ERR_USER_TO_USER_REQUIRED int32 = 69 //Ticket must be for USER-TO-USER
KDC_ERR_CANT_VERIFY_CERTIFICATE int32 = 70 //Reserved for PKINIT
KDC_ERR_INVALID_CERTIFICATE int32 = 71 //Reserved for PKINIT
KDC_ERR_REVOKED_CERTIFICATE int32 = 72 //Reserved for PKINIT
KDC_ERR_REVOCATION_STATUS_UNKNOWN int32 = 73 //Reserved for PKINIT
KDC_ERR_REVOCATION_STATUS_UNAVAILABLE int32 = 74 //Reserved for PKINIT
KDC_ERR_CLIENT_NAME_MISMATCH int32 = 75 //Reserved for PKINIT
KDC_ERR_KDC_NAME_MISMATCH int32 = 76 //Reserved for PKINIT
)
// Lookup an error code description.
func Lookup(i int32) string {
if s, ok := errorcodeLookup[i]; ok {
return fmt.Sprintf("(%d) %s", i, s)
}
return fmt.Sprintf("Unknown ErrorCode %d", i)
}
var errorcodeLookup = map[int32]string{
KDC_ERR_NONE: "KDC_ERR_NONE No error",
KDC_ERR_NAME_EXP: "KDC_ERR_NAME_EXP Client's entry in database has expired",
KDC_ERR_SERVICE_EXP: "KDC_ERR_SERVICE_EXP Server's entry in database has expired",
KDC_ERR_BAD_PVNO: "KDC_ERR_BAD_PVNO Requested protocol version number not supported",
KDC_ERR_C_OLD_MAST_KVNO: "KDC_ERR_C_OLD_MAST_KVNO Client's key encrypted in old master key",
KDC_ERR_S_OLD_MAST_KVNO: "KDC_ERR_S_OLD_MAST_KVNO Server's key encrypted in old master key",
KDC_ERR_C_PRINCIPAL_UNKNOWN: "KDC_ERR_C_PRINCIPAL_UNKNOWN Client not found in Kerberos database",
KDC_ERR_S_PRINCIPAL_UNKNOWN: "KDC_ERR_S_PRINCIPAL_UNKNOWN Server not found in Kerberos database",
KDC_ERR_PRINCIPAL_NOT_UNIQUE: "KDC_ERR_PRINCIPAL_NOT_UNIQUE Multiple principal entries in database",
KDC_ERR_NULL_KEY: "KDC_ERR_NULL_KEY The client or server has a null key",
KDC_ERR_CANNOT_POSTDATE: "KDC_ERR_CANNOT_POSTDATE Ticket not eligible for postdating",
KDC_ERR_NEVER_VALID: "KDC_ERR_NEVER_VALID Requested starttime is later than end time",
KDC_ERR_POLICY: "KDC_ERR_POLICY KDC policy rejects request",
KDC_ERR_BADOPTION: "KDC_ERR_BADOPTION KDC cannot accommodate requested option",
KDC_ERR_ETYPE_NOSUPP: "KDC_ERR_ETYPE_NOSUPP KDC has no support for encryption type",
KDC_ERR_SUMTYPE_NOSUPP: "KDC_ERR_SUMTYPE_NOSUPP KDC has no support for checksum type",
KDC_ERR_PADATA_TYPE_NOSUPP: "KDC_ERR_PADATA_TYPE_NOSUPP KDC has no support for padata type",
KDC_ERR_TRTYPE_NOSUPP: "KDC_ERR_TRTYPE_NOSUPP KDC has no support for transited type",
KDC_ERR_CLIENT_REVOKED: "KDC_ERR_CLIENT_REVOKED Clients credentials have been revoked",
KDC_ERR_SERVICE_REVOKED: "KDC_ERR_SERVICE_REVOKED Credentials for server have been revoked",
KDC_ERR_TGT_REVOKED: "KDC_ERR_TGT_REVOKED TGT has been revoked",
KDC_ERR_CLIENT_NOTYET: "KDC_ERR_CLIENT_NOTYET Client not yet valid; try again later",
KDC_ERR_SERVICE_NOTYET: "KDC_ERR_SERVICE_NOTYET Server not yet valid; try again later",
KDC_ERR_KEY_EXPIRED: "KDC_ERR_KEY_EXPIRED Password has expired; change password to reset",
KDC_ERR_PREAUTH_FAILED: "KDC_ERR_PREAUTH_FAILED Pre-authentication information was invalid",
KDC_ERR_PREAUTH_REQUIRED: "KDC_ERR_PREAUTH_REQUIRED Additional pre-authentication required",
KDC_ERR_SERVER_NOMATCH: "KDC_ERR_SERVER_NOMATCH Requested server and ticket don't match",
KDC_ERR_MUST_USE_USER2USER: "KDC_ERR_MUST_USE_USER2USER Server principal valid for user2user only",
KDC_ERR_PATH_NOT_ACCEPTED: "KDC_ERR_PATH_NOT_ACCEPTED KDC Policy rejects transited path",
KDC_ERR_SVC_UNAVAILABLE: "KDC_ERR_SVC_UNAVAILABLE A service is not available",
KRB_AP_ERR_BAD_INTEGRITY: "KRB_AP_ERR_BAD_INTEGRITY Integrity check on decrypted field failed",
KRB_AP_ERR_TKT_EXPIRED: "KRB_AP_ERR_TKT_EXPIRED Ticket expired",
KRB_AP_ERR_TKT_NYV: "KRB_AP_ERR_TKT_NYV Ticket not yet valid",
KRB_AP_ERR_REPEAT: "KRB_AP_ERR_REPEAT Request is a replay",
KRB_AP_ERR_NOT_US: "KRB_AP_ERR_NOT_US The ticket isn't for us",
KRB_AP_ERR_BADMATCH: "KRB_AP_ERR_BADMATCH Ticket and authenticator don't match",
KRB_AP_ERR_SKEW: "KRB_AP_ERR_SKEW Clock skew too great",
KRB_AP_ERR_BADADDR: "KRB_AP_ERR_BADADDR Incorrect net address",
KRB_AP_ERR_BADVERSION: "KRB_AP_ERR_BADVERSION Protocol version mismatch",
KRB_AP_ERR_MSG_TYPE: "KRB_AP_ERR_MSG_TYPE Invalid msg type",
KRB_AP_ERR_MODIFIED: "KRB_AP_ERR_MODIFIED Message stream modified",
KRB_AP_ERR_BADORDER: "KRB_AP_ERR_BADORDER Message out of order",
KRB_AP_ERR_BADKEYVER: "KRB_AP_ERR_BADKEYVER Specified version of key is not available",
KRB_AP_ERR_NOKEY: "KRB_AP_ERR_NOKEY Service key not available",
KRB_AP_ERR_MUT_FAIL: "KRB_AP_ERR_MUT_FAIL Mutual authentication failed",
KRB_AP_ERR_BADDIRECTION: "KRB_AP_ERR_BADDIRECTION Incorrect message direction",
KRB_AP_ERR_METHOD: "KRB_AP_ERR_METHOD Alternative authentication method required",
KRB_AP_ERR_BADSEQ: "KRB_AP_ERR_BADSEQ Incorrect sequence number in message",
KRB_AP_ERR_INAPP_CKSUM: "KRB_AP_ERR_INAPP_CKSUM Inappropriate type of checksum in message",
KRB_AP_PATH_NOT_ACCEPTED: "KRB_AP_PATH_NOT_ACCEPTED Policy rejects transited path",
KRB_ERR_RESPONSE_TOO_BIG: "KRB_ERR_RESPONSE_TOO_BIG Response too big for UDP; retry with TCP",
KRB_ERR_GENERIC: "KRB_ERR_GENERIC Generic error (description in e-text)",
KRB_ERR_FIELD_TOOLONG: "KRB_ERR_FIELD_TOOLONG Field is too long for this implementation",
KDC_ERROR_CLIENT_NOT_TRUSTED: "KDC_ERROR_CLIENT_NOT_TRUSTED Reserved for PKINIT",
KDC_ERROR_KDC_NOT_TRUSTED: "KDC_ERROR_KDC_NOT_TRUSTED Reserved for PKINIT",
KDC_ERROR_INVALID_SIG: "KDC_ERROR_INVALID_SIG Reserved for PKINIT",
KDC_ERR_KEY_TOO_WEAK: "KDC_ERR_KEY_TOO_WEAK Reserved for PKINIT",
KDC_ERR_CERTIFICATE_MISMATCH: "KDC_ERR_CERTIFICATE_MISMATCH Reserved for PKINIT",
KRB_AP_ERR_NO_TGT: "KRB_AP_ERR_NO_TGT No TGT available to validate USER-TO-USER",
KDC_ERR_WRONG_REALM: "KDC_ERR_WRONG_REALM Reserved for future use",
KRB_AP_ERR_USER_TO_USER_REQUIRED: "KRB_AP_ERR_USER_TO_USER_REQUIRED Ticket must be for USER-TO-USER",
KDC_ERR_CANT_VERIFY_CERTIFICATE: "KDC_ERR_CANT_VERIFY_CERTIFICATE Reserved for PKINIT",
KDC_ERR_INVALID_CERTIFICATE: "KDC_ERR_INVALID_CERTIFICATE Reserved for PKINIT",
KDC_ERR_REVOKED_CERTIFICATE: "KDC_ERR_REVOKED_CERTIFICATE Reserved for PKINIT",
KDC_ERR_REVOCATION_STATUS_UNKNOWN: "KDC_ERR_REVOCATION_STATUS_UNKNOWN Reserved for PKINIT",
KDC_ERR_REVOCATION_STATUS_UNAVAILABLE: "KDC_ERR_REVOCATION_STATUS_UNAVAILABLE Reserved for PKINIT",
KDC_ERR_CLIENT_NAME_MISMATCH: "KDC_ERR_CLIENT_NAME_MISMATCH Reserved for PKINIT",
KDC_ERR_KDC_NAME_MISMATCH: "KDC_ERR_KDC_NAME_MISMATCH Reserved for PKINIT",
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/etypeID/ 0000775 0000000 0000000 00000000000 13625372257 0023030 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/etypeID/constants.go 0000664 0000000 0000000 00000007356 13625372257 0025406 0 ustar 00root root 0000000 0000000 // Package etypeID provides Kerberos 5 encryption type assigned numbers.
package etypeID
// Kerberos encryption type assigned numbers.
const (
//RESERVED : 0
DES_CBC_CRC int32 = 1
DES_CBC_MD4 int32 = 2
DES_CBC_MD5 int32 = 3
DES_CBC_RAW int32 = 4
DES3_CBC_MD5 int32 = 5
DES3_CBC_RAW int32 = 6
DES3_CBC_SHA1 int32 = 7
DES_HMAC_SHA1 int32 = 8
DSAWITHSHA1_CMSOID int32 = 9
MD5WITHRSAENCRYPTION_CMSOID int32 = 10
SHA1WITHRSAENCRYPTION_CMSOID int32 = 11
RC2CBC_ENVOID int32 = 12
RSAENCRYPTION_ENVOID int32 = 13
RSAES_OAEP_ENV_OID int32 = 14
DES_EDE3_CBC_ENV_OID int32 = 15
DES3_CBC_SHA1_KD int32 = 16
AES128_CTS_HMAC_SHA1_96 int32 = 17
AES256_CTS_HMAC_SHA1_96 int32 = 18
AES128_CTS_HMAC_SHA256_128 int32 = 19
AES256_CTS_HMAC_SHA384_192 int32 = 20
//UNASSIGNED : 21-22
RC4_HMAC int32 = 23
RC4_HMAC_EXP int32 = 24
CAMELLIA128_CTS_CMAC int32 = 25
CAMELLIA256_CTS_CMAC int32 = 26
//UNASSIGNED : 27-64
SUBKEY_KEYMATERIAL int32 = 65
//UNASSIGNED : 66-2147483647
)
// ETypesByName is a map of EncType names to their assigned EncType number.
var ETypesByName = map[string]int32{
"des-cbc-crc": DES_CBC_CRC,
"des-cbc-md4": DES_CBC_MD4,
"des-cbc-md5": DES_CBC_MD5,
"des-cbc-raw": DES_CBC_RAW,
"des3-cbc-md5": DES3_CBC_MD5,
"des3-cbc-raw": DES3_CBC_RAW,
"des3-cbc-sha1": DES3_CBC_SHA1,
"des3-hmac-sha1": DES_HMAC_SHA1,
"des3-cbc-sha1-kd": DES3_CBC_SHA1_KD,
"des-hmac-sha1": DES_HMAC_SHA1,
"dsaWithSHA1-CmsOID": DSAWITHSHA1_CMSOID,
"md5WithRSAEncryption-CmsOID": MD5WITHRSAENCRYPTION_CMSOID,
"sha1WithRSAEncryption-CmsOID": SHA1WITHRSAENCRYPTION_CMSOID,
"rc2CBC-EnvOID": RC2CBC_ENVOID,
"rsaEncryption-EnvOID": RSAENCRYPTION_ENVOID,
"rsaES-OAEP-ENV-OID": RSAES_OAEP_ENV_OID,
"des-ede3-cbc-Env-OID": DES_EDE3_CBC_ENV_OID,
"aes128-cts-hmac-sha1-96": AES128_CTS_HMAC_SHA1_96,
"aes128-cts": AES128_CTS_HMAC_SHA1_96,
"aes128-sha1": AES128_CTS_HMAC_SHA1_96,
"aes256-cts-hmac-sha1-96": AES256_CTS_HMAC_SHA1_96,
"aes256-cts": AES128_CTS_HMAC_SHA1_96,
"aes256-sha1": AES128_CTS_HMAC_SHA1_96,
"aes128-cts-hmac-sha256-128": AES128_CTS_HMAC_SHA256_128,
"aes128-sha2": AES128_CTS_HMAC_SHA256_128,
"aes256-cts-hmac-sha384-192": AES256_CTS_HMAC_SHA384_192,
"aes256-sha2": AES256_CTS_HMAC_SHA384_192,
"arcfour-hmac": RC4_HMAC,
"rc4-hmac": RC4_HMAC,
"arcfour-hmac-md5": RC4_HMAC,
"arcfour-hmac-exp": RC4_HMAC_EXP,
"rc4-hmac-exp": RC4_HMAC_EXP,
"arcfour-hmac-md5-exp": RC4_HMAC_EXP,
"camellia128-cts-cmac": CAMELLIA128_CTS_CMAC,
"camellia128-cts": CAMELLIA128_CTS_CMAC,
"camellia256-cts-cmac": CAMELLIA256_CTS_CMAC,
"camellia256-cts": CAMELLIA256_CTS_CMAC,
"subkey-keymaterial": SUBKEY_KEYMATERIAL,
}
// EtypeSupported resolves the etype name string to the etype ID.
// If zero is returned the etype is not supported by gokrb5.
func EtypeSupported(etype string) int32 {
// Slice of supported enctype IDs
s := []int32{
AES128_CTS_HMAC_SHA1_96,
AES256_CTS_HMAC_SHA1_96,
AES128_CTS_HMAC_SHA256_128,
AES256_CTS_HMAC_SHA384_192,
DES3_CBC_SHA1_KD,
RC4_HMAC,
}
id := ETypesByName[etype]
if id == 0 {
return id
}
for _, sid := range s {
if id == sid {
return id
}
}
return 0
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/flags/ 0000775 0000000 0000000 00000000000 13625372257 0022561 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/flags/constants.go 0000664 0000000 0000000 00000001460 13625372257 0025125 0 ustar 00root root 0000000 0000000 // Package flags provides Kerberos 5 flag assigned numbers.
package flags
// Flag values for KRB5 messages and tickets.
const (
Reserved = 0
Forwardable = 1
Forwarded = 2
Proxiable = 3
Proxy = 4
AllowPostDate = 5
MayPostDate = 5
PostDated = 6
Invalid = 7
Renewable = 8
Initial = 9
PreAuthent = 10
HWAuthent = 11
OptHardwareAuth = 11
RequestAnonymous = 12
TransitedPolicyChecked = 12
OKAsDelegate = 13
EncPARep = 15
Canonicalize = 15
DisableTransitedCheck = 26
RenewableOK = 27
EncTktInSkey = 28
Renew = 30
Validate = 31
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/keyusage/ 0000775 0000000 0000000 00000000000 13625372257 0023302 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/keyusage/constants.go 0000664 0000000 0000000 00000003635 13625372257 0025654 0 ustar 00root root 0000000 0000000 // Package keyusage provides Kerberos 5 key usage assigned numbers.
package keyusage
// Key usage numbers.
const (
AS_REQ_PA_ENC_TIMESTAMP = 1
KDC_REP_TICKET = 2
AS_REP_ENCPART = 3
TGS_REQ_KDC_REQ_BODY_AUTHDATA_SESSION_KEY = 4
TGS_REQ_KDC_REQ_BODY_AUTHDATA_SUB_KEY = 5
TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM = 6
TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR = 7
TGS_REP_ENCPART_SESSION_KEY = 8
TGS_REP_ENCPART_AUTHENTICATOR_SUB_KEY = 9
AP_REQ_AUTHENTICATOR_CHKSUM = 10
AP_REQ_AUTHENTICATOR = 11
AP_REP_ENCPART = 12
KRB_PRIV_ENCPART = 13
KRB_CRED_ENCPART = 14
KRB_SAFE_CHKSUM = 15
KERB_NON_KERB_SALT = 16
KERB_NON_KERB_CKSUM_SALT = 17
//18. Reserved for future use in Kerberos and related protocols.
AD_KDC_ISSUED_CHKSUM = 19
//20-21. Reserved for future use in Kerberos and related protocols.
GSSAPI_ACCEPTOR_SEAL = 22
GSSAPI_ACCEPTOR_SIGN = 23
GSSAPI_INITIATOR_SEAL = 24
GSSAPI_INITIATOR_SIGN = 25
KEY_USAGE_FAST_REQ_CHKSUM = 50
KEY_USAGE_FAST_ENC = 51
KEY_USAGE_FAST_REP = 52
KEY_USAGE_FAST_FINISHED = 53
KEY_USAGE_ENC_CHALLENGE_CLIENT = 54
KEY_USAGE_ENC_CHALLENGE_KDC = 55
KEY_USAGE_AS_REQ = 56
//26-511. Reserved for future use in Kerberos and related protocols.
//512-1023. Reserved for uses internal to a Kerberos implementation.
//1024. Encryption for application use in protocols that do not specify key usage values
//1025. Checksums for application use in protocols that do not specify key usage values
//1026-2047. Reserved for application use.
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/msgtype/ 0000775 0000000 0000000 00000000000 13625372257 0023155 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/msgtype/constants.go 0000664 0000000 0000000 00000001471 13625372257 0025523 0 ustar 00root root 0000000 0000000 // Package msgtype provides Kerberos 5 message type assigned numbers.
package msgtype
// KRB message type IDs.
const (
KRB_AS_REQ = 10 //Request for initial authentication
KRB_AS_REP = 11 //Response to KRB_AS_REQ request
KRB_TGS_REQ = 12 //Request for authentication based on TGT
KRB_TGS_REP = 13 //Response to KRB_TGS_REQ request
KRB_AP_REQ = 14 //Application request to server
KRB_AP_REP = 15 //Response to KRB_AP_REQ_MUTUAL
KRB_RESERVED16 = 16 //Reserved for user-to-user krb_tgt_request
KRB_RESERVED17 = 17 //Reserved for user-to-user krb_tgt_reply
KRB_SAFE = 20 // Safe (checksummed) application message
KRB_PRIV = 21 // Private (encrypted) application message
KRB_CRED = 22 //Private (encrypted) message to forward credentials
KRB_ERROR = 30 //Error response
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/nametype/ 0000775 0000000 0000000 00000000000 13625372257 0023307 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/nametype/constants.go 0000664 0000000 0000000 00000001467 13625372257 0025662 0 ustar 00root root 0000000 0000000 // Package nametype provides Kerberos 5 principal name type numbers.
package nametype
// Kerberos name type IDs.
const (
KRB_NT_UNKNOWN int32 = 0 //Name type not known
KRB_NT_PRINCIPAL int32 = 1 //Just the name of the principal as in DCE, or for users
KRB_NT_SRV_INST int32 = 2 //Service and other unique instance (krbtgt)
KRB_NT_SRV_HST int32 = 3 //Service with host name as instance (telnet, rcommands)
KRB_NT_SRV_XHST int32 = 4 //Service with host as remaining components
KRB_NT_UID int32 = 5 //Unique ID
KRB_NT_X500_PRINCIPAL int32 = 6 //Encoded X.509 Distinguished name [RFC2253]
KRB_NT_SMTP_NAME int32 = 7 //Name in form of SMTP email name (e.g., user@example.com)
KRB_NT_ENTERPRISE int32 = 10 //Enterprise name; may be mapped to principal name
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/patype/ 0000775 0000000 0000000 00000000000 13625372257 0022767 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/patype/constants.go 0000664 0000000 0000000 00000004722 13625372257 0025337 0 ustar 00root root 0000000 0000000 // Package patype provides Kerberos 5 pre-authentication type assigned numbers.
package patype
// Kerberos pre-authentication type assigned numbers.
const (
PA_TGS_REQ int32 = 1
PA_ENC_TIMESTAMP int32 = 2
PA_PW_SALT int32 = 3
//RESERVED : 4
PA_ENC_UNIX_TIME int32 = 5
PA_SANDIA_SECUREID int32 = 6
PA_SESAME int32 = 7
PA_OSF_DCE int32 = 8
PA_CYBERSAFE_SECUREID int32 = 9
PA_AFS3_SALT int32 = 10
PA_ETYPE_INFO int32 = 11
PA_SAM_CHALLENGE int32 = 12
PA_SAM_RESPONSE int32 = 13
PA_PK_AS_REQ_OLD int32 = 14
PA_PK_AS_REP_OLD int32 = 15
PA_PK_AS_REQ int32 = 16
PA_PK_AS_REP int32 = 17
PA_PK_OCSP_RESPONSE int32 = 18
PA_ETYPE_INFO2 int32 = 19
PA_USE_SPECIFIED_KVNO int32 = 20
PA_SVR_REFERRAL_INFO int32 = 20
PA_SAM_REDIRECT int32 = 21
PA_GET_FROM_TYPED_DATA int32 = 22
TD_PADATA int32 = 22
PA_SAM_ETYPE_INFO int32 = 23
PA_ALT_PRINC int32 = 24
PA_SERVER_REFERRAL int32 = 25
//UNASSIGNED : 26-29
PA_SAM_CHALLENGE2 int32 = 30
PA_SAM_RESPONSE2 int32 = 31
//UNASSIGNED : 32-40
PA_EXTRA_TGT int32 = 41
//UNASSIGNED : 42-100
TD_PKINIT_CMS_CERTIFICATES int32 = 101
TD_KRB_PRINCIPAL int32 = 102
TD_KRB_REALM int32 = 103
TD_TRUSTED_CERTIFIERS int32 = 104
TD_CERTIFICATE_INDEX int32 = 105
TD_APP_DEFINED_ERROR int32 = 106
TD_REQ_NONCE int32 = 107
TD_REQ_SEQ int32 = 108
TD_DH_PARAMETERS int32 = 109
//UNASSIGNED : 110
TD_CMS_DIGEST_ALGORITHMS int32 = 111
TD_CERT_DIGEST_ALGORITHMS int32 = 112
//UNASSIGNED : 113-127
PA_PAC_REQUEST int32 = 128
PA_FOR_USER int32 = 129
PA_FOR_X509_USER int32 = 130
PA_FOR_CHECK_DUPS int32 = 131
PA_AS_CHECKSUM int32 = 132
PA_FX_COOKIE int32 = 133
PA_AUTHENTICATION_SET int32 = 134
PA_AUTH_SET_SELECTED int32 = 135
PA_FX_FAST int32 = 136
PA_FX_ERROR int32 = 137
PA_ENCRYPTED_CHALLENGE int32 = 138
//UNASSIGNED : 139-140
PA_OTP_CHALLENGE int32 = 141
PA_OTP_REQUEST int32 = 142
PA_OTP_CONFIRM int32 = 143
PA_OTP_PIN_CHANGE int32 = 144
PA_EPAK_AS_REQ int32 = 145
PA_EPAK_AS_REP int32 = 146
PA_PKINIT_KX int32 = 147
PA_PKU2U_NAME int32 = 148
PA_REQ_ENC_PA_REP int32 = 149
PA_AS_FRESHNESS int32 = 150
//UNASSIGNED : 151-164
PA_SUPPORTED_ETYPES int32 = 165
PA_EXTENDED_ERROR int32 = 166
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/trtype/ 0000775 0000000 0000000 00000000000 13625372257 0023014 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/trtype/constants.go 0000664 0000000 0000000 00000000315 13625372257 0025356 0 ustar 00root root 0000000 0000000 // Package trtype provides Transited Encoding Type assigned numbers.
package trtype
// Transited Encoding Type IDs.
const (
DOMAIN_X500_COMPRESS int32 = 1
//Reserved values All others
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/ 0000775 0000000 0000000 00000000000 13625372257 0022020 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/changepasswddata.go 0000664 0000000 0000000 00000001202 13625372257 0025643 0 ustar 00root root 0000000 0000000 package kadmin
import (
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// ChangePasswdData is the payload to a password change message.
type ChangePasswdData struct {
NewPasswd []byte `asn1:"explicit,tag:0"`
TargName types.PrincipalName `asn1:"explicit,optional,tag:1"`
TargRealm string `asn1:"generalstring,optional,explicit,tag:2"`
}
// Marshal ChangePasswdData into a byte slice.
func (c *ChangePasswdData) Marshal() ([]byte, error) {
b, err := asn1.Marshal(*c)
if err != nil {
return []byte{}, err
}
//b = asn1tools.AddASNAppTag(b, asnAppTag.)
return b, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/changepasswddata_test.go 0000664 0000000 0000000 00000001462 13625372257 0026712 0 ustar 00root root 0000000 0000000 package kadmin
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
func TestChangePasswdData_Marshal(t *testing.T) {
t.Parallel()
chgpasswd := ChangePasswdData{
NewPasswd: []byte("newpassword"),
TargName: types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, "testuser1"),
TargRealm: "TEST.GOKRB5",
}
chpwdb, err := chgpasswd.Marshal()
if err != nil {
t.Fatalf("error marshaling change passwd data: %v\n", err)
}
v := "ChangePasswdData"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
assert.Equal(t, b, chpwdb, "marshaled bytes of change passwd data not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/message.go 0000664 0000000 0000000 00000005303 13625372257 0023774 0 ustar 00root root 0000000 0000000 package kadmin
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"math"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
const (
verisonHex = "ff80"
)
// Request message for changing password.
type Request struct {
APREQ messages.APReq
KRBPriv messages.KRBPriv
}
// Reply message for a password change.
type Reply struct {
MessageLength int
Version int
APREPLength int
APREP messages.APRep
KRBPriv messages.KRBPriv
KRBError messages.KRBError
IsKRBError bool
ResultCode uint16
Result string
}
// Marshal a Request into a byte slice.
func (m *Request) Marshal() (b []byte, err error) {
b = []byte{255, 128} // protocol version number: contains the hex constant 0xff80 (big-endian integer).
ab, e := m.APREQ.Marshal()
if e != nil {
err = fmt.Errorf("error marshaling AP_REQ: %v", e)
return
}
if len(ab) > math.MaxUint16 {
err = errors.New("length of AP_REQ greater then max Uint16 size")
return
}
al := make([]byte, 2)
binary.BigEndian.PutUint16(al, uint16(len(ab)))
b = append(b, al...)
b = append(b, ab...)
pb, e := m.KRBPriv.Marshal()
if e != nil {
err = fmt.Errorf("error marshaling KRB_Priv: %v", e)
return
}
b = append(b, pb...)
if len(b)+2 > math.MaxUint16 {
err = errors.New("length of message greater then max Uint16 size")
return
}
ml := make([]byte, 2)
binary.BigEndian.PutUint16(ml, uint16(len(b)+2))
b = append(ml, b...)
return
}
// Unmarshal a byte slice into a Reply.
func (m *Reply) Unmarshal(b []byte) error {
m.MessageLength = int(binary.BigEndian.Uint16(b[0:2]))
m.Version = int(binary.BigEndian.Uint16(b[2:4]))
if m.Version != 1 {
return fmt.Errorf("kadmin reply has incorrect protocol version number: %d", m.Version)
}
m.APREPLength = int(binary.BigEndian.Uint16(b[4:6]))
if m.APREPLength != 0 {
err := m.APREP.Unmarshal(b[6 : 6+m.APREPLength])
if err != nil {
return err
}
err = m.KRBPriv.Unmarshal(b[6+m.APREPLength : m.MessageLength])
if err != nil {
return err
}
} else {
m.IsKRBError = true
m.KRBError.Unmarshal(b[6:m.MessageLength])
m.ResultCode, m.Result = parseResponse(m.KRBError.EData)
}
return nil
}
func parseResponse(b []byte) (c uint16, s string) {
c = binary.BigEndian.Uint16(b[0:2])
buf := bytes.NewBuffer(b[2:])
m := make([]byte, len(b)-2)
binary.Read(buf, binary.BigEndian, &m)
s = string(m)
return
}
// Decrypt the encrypted part of the KRBError within the change password Reply.
func (m *Reply) Decrypt(key types.EncryptionKey) error {
if m.IsKRBError {
return m.KRBError
}
err := m.KRBPriv.DecryptEncPart(key)
if err != nil {
return err
}
m.ResultCode, m.Result = parseResponse(m.KRBPriv.DecryptedEncPart.UserData)
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/message_test.go 0000664 0000000 0000000 00000002555 13625372257 0025041 0 ustar 00root root 0000000 0000000 package kadmin
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestUnmarshalReply(t *testing.T) {
t.Parallel()
var a Reply
v := "Kpasswd_Rep"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, 236, a.MessageLength, "message length not as expected")
assert.Equal(t, 1, a.Version, "message version not as expected")
assert.Equal(t, 140, a.APREPLength, "AP_REP length not as expected")
assert.Equal(t, iana.PVNO, a.APREP.PVNO, "AP_REP within reply not as expected")
assert.Equal(t, msgtype.KRB_AP_REP, a.APREP.MsgType, "AP_REP message type within reply not as expected")
assert.Equal(t, int32(18), a.APREP.EncPart.EType, "AP_REQ etype not as expected")
assert.Equal(t, iana.PVNO, a.KRBPriv.PVNO, "KRBPriv within reply not as expected")
assert.Equal(t, msgtype.KRB_PRIV, a.KRBPriv.MsgType, "KRBPriv type within reply not as expected")
assert.Equal(t, int32(18), a.KRBPriv.EncPart.EType, "KRBPriv etype not as expected")
}
// Request marshal is tested via integration test in the client package due to the dynamic keys and encryption.
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/passwd.go 0000664 0000000 0000000 00000003661 13625372257 0023656 0 ustar 00root root 0000000 0000000 // Package kadmin provides Kerberos administration capabilities.
package kadmin
import (
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// ChangePasswdMsg generate a change password request and also return the key needed to decrypt the reply.
func ChangePasswdMsg(cname types.PrincipalName, realm, password string, tkt messages.Ticket, sessionKey types.EncryptionKey) (r Request, k types.EncryptionKey, err error) {
// Create change password data struct and marshal to bytes
chgpasswd := ChangePasswdData{
NewPasswd: []byte(password),
TargName: cname,
TargRealm: realm,
}
chpwdb, err := chgpasswd.Marshal()
if err != nil {
err = krberror.Errorf(err, krberror.KRBMsgError, "error marshaling change passwd data")
return
}
// Generate authenticator
auth, err := types.NewAuthenticator(realm, cname)
if err != nil {
err = krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
return
}
etype, err := crypto.GetEtype(sessionKey.KeyType)
if err != nil {
err = krberror.Errorf(err, krberror.KRBMsgError, "error generating subkey etype")
return
}
err = auth.GenerateSeqNumberAndSubKey(etype.GetETypeID(), etype.GetKeyByteSize())
if err != nil {
err = krberror.Errorf(err, krberror.KRBMsgError, "error generating subkey")
return
}
k = auth.SubKey
// Generate AP_REQ
APreq, err := messages.NewAPReq(tkt, sessionKey, auth)
if err != nil {
return
}
// Form the KRBPriv encpart data
kp := messages.EncKrbPrivPart{
UserData: chpwdb,
Timestamp: auth.CTime,
Usec: auth.Cusec,
SequenceNumber: auth.SeqNumber,
}
kpriv := messages.NewKRBPriv(kp)
err = kpriv.EncryptEncPart(k)
if err != nil {
err = krberror.Errorf(err, krberror.EncryptingError, "error encrypting change passwd data")
return
}
r = Request{
APREQ: APreq,
KRBPriv: kpriv,
}
return
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/keytab/ 0000775 0000000 0000000 00000000000 13625372257 0022034 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/keytab/keytab.go 0000664 0000000 0000000 00000021061 13625372257 0023642 0 ustar 00root root 0000000 0000000 // Package keytab implements Kerberos keytabs: https://web.mit.edu/kerberos/krb5-devel/doc/formats/keytab_file_format.html.
package keytab
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"time"
"unsafe"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
const (
keytabFirstByte byte = 05
)
// Keytab struct.
type Keytab struct {
Version uint8
Entries []entry
}
// Keytab entry struct.
type entry struct {
Principal principal
Timestamp time.Time
KVNO8 uint8
Key types.EncryptionKey
KVNO uint32
}
// Keytab entry principal struct.
type principal struct {
NumComponents int16
Realm string
Components []string
NameType int32
}
// NewKeytab creates new, empty Keytab type.
func NewKeytab() Keytab {
var e []entry
return Keytab{
Version: 0,
Entries: e,
}
}
// GetEncryptionKey returns the EncryptionKey from the Keytab for the newest entry with the required kvno, etype and matching principal.
func (kt *Keytab) GetEncryptionKey(nameString []string, realm string, kvno int, etype int32) (types.EncryptionKey, error) {
var key types.EncryptionKey
var t time.Time
for _, k := range kt.Entries {
if k.Principal.Realm == realm && len(k.Principal.Components) == len(nameString) &&
k.Key.KeyType == etype &&
(k.KVNO == uint32(kvno) || kvno == 0) &&
k.Timestamp.After(t) {
p := true
for i, n := range k.Principal.Components {
if nameString[i] != n {
p = false
break
}
}
if p {
key = k.Key
t = k.Timestamp
}
}
}
if len(key.KeyValue) < 1 {
return key, fmt.Errorf("matching key not found in keytab. Looking for %v realm: %v kvno: %v etype: %v", nameString, realm, kvno, etype)
}
return key, nil
}
// Create a new Keytab entry.
func newKeytabEntry() entry {
var b []byte
return entry{
Principal: newPrincipal(),
Timestamp: time.Time{},
KVNO8: 0,
Key: types.EncryptionKey{
KeyType: 0,
KeyValue: b,
},
KVNO: 0,
}
}
// Create a new principal.
func newPrincipal() principal {
var c []string
return principal{
NumComponents: 0,
Realm: "",
Components: c,
NameType: 0,
}
}
// Load a Keytab file into a Keytab type.
func Load(ktPath string) (kt Keytab, err error) {
k, err := ioutil.ReadFile(ktPath)
if err != nil {
return
}
return Parse(k)
}
// Marshal keytab into byte slice
func (kt Keytab) Marshal() ([]byte, error) {
b := []byte{keytabFirstByte, kt.Version}
for _, e := range kt.Entries {
eb, err := e.marshal(int(kt.Version))
if err != nil {
return b, err
}
b = append(b, eb...)
}
return b, nil
}
// Write the keytab bytes to io.Writer.
// Returns the number of bytes written
func (kt Keytab) Write(w io.Writer) (int, error) {
b, err := kt.Marshal()
if err != nil {
return 0, fmt.Errorf("error marshaling keytab: %v", err)
}
return w.Write(b)
}
// Parse byte slice of Keytab data into Keytab type.
func Parse(b []byte) (kt Keytab, err error) {
//The first byte of the file always has the value 5
if b[0] != keytabFirstByte {
err = errors.New("invalid keytab data. First byte does not equal 5")
return
}
//Get keytab version
//The 2nd byte contains the version number (1 or 2)
kt.Version = uint8(b[1])
if kt.Version != 1 && kt.Version != 2 {
err = errors.New("invalid keytab data. Keytab version is neither 1 nor 2")
return
}
//Version 1 of the file format uses native byte order for integer representations. Version 2 always uses big-endian byte order
var endian binary.ByteOrder
endian = binary.BigEndian
if kt.Version == 1 && isNativeEndianLittle() {
endian = binary.LittleEndian
}
// n tracks position in the byte array
n := 2
l := readInt32(b, &n, &endian)
for l != 0 {
if l < 0 {
//Zero padded so skip over
l = l * -1
n = n + int(l)
} else {
//fmt.Printf("Bytes for entry: %v\n", b[n:n+int(l)])
eb := b[n : n+int(l)]
n = n + int(l)
ke := newKeytabEntry()
// p keeps track as to where we are in the byte stream
var p int
parsePrincipal(eb, &p, &kt, &ke, &endian)
ke.Timestamp = readTimestamp(eb, &p, &endian)
ke.KVNO8 = uint8(readInt8(eb, &p, &endian))
ke.Key.KeyType = int32(readInt16(eb, &p, &endian))
kl := int(readInt16(eb, &p, &endian))
ke.Key.KeyValue = readBytes(eb, &p, kl, &endian)
if len(eb)-p >= 4 {
// The 32-bit key may be present
ke.KVNO = uint32(readInt32(eb, &p, &endian))
}
if ke.KVNO == 0 {
// Handles if the value from the last 4 bytes was zero and also if there are not the 4 bytes present. Makes sense to put the same value here as KVNO8
ke.KVNO = uint32(ke.KVNO8)
}
// Add the entry to the keytab
kt.Entries = append(kt.Entries, ke)
}
// Check if there are still 4 bytes left to read
if n > len(b) || len(b[n:]) < 4 {
break
}
// Read the size of the next entry
l = readInt32(b, &n, &endian)
}
return
}
func (e entry) marshal(v int) ([]byte, error) {
var b []byte
pb, err := e.Principal.marshal(v)
if err != nil {
return b, err
}
b = append(b, pb...)
var endian binary.ByteOrder
endian = binary.BigEndian
if v == 1 && isNativeEndianLittle() {
endian = binary.LittleEndian
}
t := make([]byte, 9)
endian.PutUint32(t[0:4], uint32(e.Timestamp.Unix()))
t[4] = byte(e.KVNO8)
endian.PutUint16(t[5:7], uint16(e.Key.KeyType))
endian.PutUint16(t[7:9], uint16(len(e.Key.KeyValue)))
b = append(b, t...)
buf := new(bytes.Buffer)
err = binary.Write(buf, endian, e.Key.KeyValue)
if err != nil {
return b, err
}
b = append(b, buf.Bytes()...)
t = make([]byte, 4)
endian.PutUint32(t, e.KVNO)
b = append(b, t...)
// Add the length header
t = make([]byte, 4)
endian.PutUint32(t, uint32(len(b)))
b = append(t, b...)
return b, nil
}
// Parse the Keytab bytes of a principal into a Keytab entry's principal.
func parsePrincipal(b []byte, p *int, kt *Keytab, ke *entry, e *binary.ByteOrder) error {
ke.Principal.NumComponents = readInt16(b, p, e)
if kt.Version == 1 {
//In version 1 the number of components includes the realm. Minus 1 to make consistent with version 2
ke.Principal.NumComponents--
}
lenRealm := readInt16(b, p, e)
ke.Principal.Realm = string(readBytes(b, p, int(lenRealm), e))
for i := 0; i < int(ke.Principal.NumComponents); i++ {
l := readInt16(b, p, e)
ke.Principal.Components = append(ke.Principal.Components, string(readBytes(b, p, int(l), e)))
}
if kt.Version != 1 {
//Name Type is omitted in version 1
ke.Principal.NameType = readInt32(b, p, e)
}
return nil
}
func (p principal) marshal(v int) ([]byte, error) {
//var b []byte
b := make([]byte, 2)
var endian binary.ByteOrder
endian = binary.BigEndian
if v == 1 && isNativeEndianLittle() {
endian = binary.LittleEndian
}
endian.PutUint16(b[0:], uint16(p.NumComponents))
realm, err := marshalString(p.Realm, v)
if err != nil {
return b, err
}
b = append(b, realm...)
for _, c := range p.Components {
cb, err := marshalString(c, v)
if err != nil {
return b, err
}
b = append(b, cb...)
}
if v != 1 {
t := make([]byte, 4)
endian.PutUint32(t, uint32(p.NameType))
b = append(b, t...)
}
return b, nil
}
func marshalString(s string, v int) ([]byte, error) {
sb := []byte(s)
b := make([]byte, 2)
var endian binary.ByteOrder
endian = binary.BigEndian
if v == 1 && isNativeEndianLittle() {
endian = binary.LittleEndian
}
endian.PutUint16(b[0:], uint16(len(sb)))
buf := new(bytes.Buffer)
err := binary.Write(buf, endian, sb)
if err != nil {
return b, err
}
b = append(b, buf.Bytes()...)
return b, err
}
// 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
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/keytab/keytab_test.go 0000664 0000000 0000000 00000003332 13625372257 0024702 0 ustar 00root root 0000000 0000000 package keytab
import (
"encoding/hex"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestParse(t *testing.T) {
t.Parallel()
dat, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, err := Parse(dat)
if err != nil {
t.Fatalf("Error parsing keytab data: %v\n", err)
}
assert.Equal(t, uint8(2), kt.Version, "Keytab version not as expected")
assert.Equal(t, uint32(1), kt.Entries[0].KVNO, "KVNO not as expected")
assert.Equal(t, uint8(1), kt.Entries[0].KVNO8, "KVNO8 not as expected")
assert.Equal(t, time.Unix(1505669592, 0), kt.Entries[0].Timestamp, "Timestamp not as expected")
assert.Equal(t, int32(17), kt.Entries[0].Key.KeyType, "Key's EType not as expected")
assert.Equal(t, "698c4df8e9f60e7eea5a21bf4526ad25", hex.EncodeToString(kt.Entries[0].Key.KeyValue), "Key material not as expected")
assert.Equal(t, int16(1), kt.Entries[0].Principal.NumComponents, "Number of components in principal not as expected")
assert.Equal(t, int32(1), kt.Entries[0].Principal.NameType, "Name type of principal not as expected")
assert.Equal(t, "TEST.GOKRB5", kt.Entries[0].Principal.Realm, "Realm of principal not as expected")
assert.Equal(t, "testuser1", kt.Entries[0].Principal.Components[0], "Component in principal not as expected")
}
func TestMarshal(t *testing.T) {
t.Parallel()
dat, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, err := Parse(dat)
if err != nil {
t.Fatalf("Error parsing keytab data: %v\n", err)
}
b, err := kt.Marshal()
if err != nil {
t.Fatalf("Error marshaling: %v", err)
}
assert.Equal(t, dat, b, "Marshaled bytes not the same as input bytes")
_, err = Parse(b)
if err != nil {
t.Fatalf("Error parsing marshaled bytes: %v", err)
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/krberror/ 0000775 0000000 0000000 00000000000 13625372257 0022405 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/krberror/error.go 0000664 0000000 0000000 00000003230 13625372257 0024063 0 ustar 00root root 0000000 0000000 // Package krberror provides error type and functions for gokrb5.
package krberror
import (
"fmt"
"strings"
)
// Error type descriptions.
const (
separator = " < "
EncodingError = "Encoding_Error"
NetworkingError = "Networking_Error"
DecryptingError = "Decrypting_Error"
EncryptingError = "Encrypting_Error"
ChksumError = "Checksum_Error"
KRBMsgError = "KRBMessage_Handling_Error"
ConfigError = "Configuration_Error"
KDCError = "KDC_Error"
)
// Krberror is an error type for gokrb5
type Krberror struct {
RootCause string
EText []string
}
// Error function to implement the error interface.
func (e Krberror) Error() string {
return fmt.Sprintf("[Root cause: %s] ", e.RootCause) + strings.Join(e.EText, separator)
}
// Add another error statement to the error.
func (e *Krberror) Add(et string, s string) {
e.EText = append([]string{fmt.Sprintf("%s: %s", et, s)}, e.EText...)
}
// NewKrberror creates a new instance of Krberror.
func NewKrberror(et, s string) Krberror {
return Krberror{
RootCause: et,
EText: []string{s},
}
}
// Errorf appends to or creates a new Krberror.
func Errorf(err error, et, format string, a ...interface{}) Krberror {
if e, ok := err.(Krberror); ok {
e.Add(et, fmt.Sprintf(format, a...))
return e
}
return NewErrorf(et, format+": %s", append(a, err)...)
}
// NewErrorf creates a new Krberror from a formatted string.
func NewErrorf(et, format string, a ...interface{}) Krberror {
var s string
if len(a) > 0 {
s = fmt.Sprintf("%s: %s", et, fmt.Sprintf(format, a...))
} else {
s = fmt.Sprintf("%s: %s", et, format)
}
return Krberror{
RootCause: et,
EText: []string{s},
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/krberror/error_test.go 0000664 0000000 0000000 00000001422 13625372257 0025123 0 ustar 00root root 0000000 0000000 package krberror
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestErrorf(t *testing.T) {
err := fmt.Errorf("an error")
var a Krberror
a = Errorf(err, "cause", "some text")
assert.Equal(t, "[Root cause: cause] cause: some text: an error", a.Error())
a = Errorf(err, "cause", "arg1=%d arg2=%s", 123, "arg")
assert.Equal(t, "[Root cause: cause] cause: arg1=123 arg2=arg: an error", a.Error())
err = NewErrorf("another error", "some text")
a = Errorf(err, "cause", "some text")
assert.Equal(t, "[Root cause: another error] cause: some text < another error: some text", a.Error())
a = Errorf(err, "cause", "arg1=%d arg2=%s", 123, "arg")
assert.Equal(t, "[Root cause: another error] cause: arg1=123 arg2=arg < another error: some text", a.Error())
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/ 0000775 0000000 0000000 00000000000 13625372257 0022364 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/APRep.go 0000664 0000000 0000000 00000003267 13625372257 0023672 0 ustar 00root root 0000000 0000000 package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// APRep implements RFC 4120 KRB_AP_REP: https://tools.ietf.org/html/rfc4120#section-5.5.2.
type APRep struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
EncPart types.EncryptedData `asn1:"explicit,tag:2"`
}
// EncAPRepPart is the encrypted part of KRB_AP_REP.
type EncAPRepPart struct {
CTime time.Time `asn1:"generalized,explicit,tag:0"`
Cusec int `asn1:"explicit,tag:1"`
Subkey types.EncryptionKey `asn1:"optional,explicit,tag:2"`
SequenceNumber int64 `asn1:"optional,explicit,tag:3"`
}
// Unmarshal bytes b into the APRep struct.
func (a *APRep) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, a, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.APREP))
if err != nil {
return processUnmarshalReplyError(b, err)
}
expectedMsgType := msgtype.KRB_AP_REP
if a.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_AP_REP. Expected: %v; Actual: %v", expectedMsgType, a.MsgType)
}
return nil
}
// Unmarshal bytes b into the APRep encrypted part struct.
func (a *EncAPRepPart) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, a, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncAPRepPart))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "AP_REP unmarshal error")
}
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/APRep_test.go 0000664 0000000 0000000 00000004571 13625372257 0024730 0 ustar 00root root 0000000 0000000 package messages
import (
"encoding/hex"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestUnmarshalAPRep(t *testing.T) {
t.Parallel()
var a APRep
v := "encode_krb5_ap_rep"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_AP_REP, a.MsgType, "MsgType is not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Ticket encPart etype not as expected")
assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "Ticket encPart KVNO not as expected")
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.EncPart.Cipher, "Ticket encPart cipher not as expected")
}
func TestUnmarshalEncAPRepPart(t *testing.T) {
t.Parallel()
var a EncAPRepPart
v := "encode_krb5_ap_rep_enc_part"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, tt, a.CTime, "CTime not as expected")
assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected")
assert.Equal(t, int32(1), a.Subkey.KeyType, "Subkey type not as expected")
assert.Equal(t, []byte("12345678"), a.Subkey.KeyValue, "Subkey value not as expected")
assert.Equal(t, int64(17), a.SequenceNumber, "Sequence number not as expected")
}
func TestUnmarshalEncAPRepPart_optionalsNULL(t *testing.T) {
t.Parallel()
var a EncAPRepPart
v := "encode_krb5_ap_rep_enc_part(optionalsNULL)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, tt, a.CTime, "CTime not as expected")
assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/APReq.go 0000664 0000000 0000000 00000010646 13625372257 0023672 0 ustar 00root root 0000000 0000000 package messages
import (
"fmt"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/asn1tools"
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag"
"gopkg.in/jcmturner/gokrb5.v5/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
type marshalAPReq struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
APOptions asn1.BitString `asn1:"explicit,tag:2"`
// Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
Ticket asn1.RawValue `asn1:"explicit,tag:3"`
Authenticator types.EncryptedData `asn1:"explicit,tag:4"`
}
// APReq implements RFC 4120 KRB_AP_REQ: https://tools.ietf.org/html/rfc4120#section-5.5.1.
type APReq struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
APOptions asn1.BitString `asn1:"explicit,tag:2"`
Ticket Ticket `asn1:"explicit,tag:3"`
Authenticator types.EncryptedData `asn1:"explicit,tag:4"`
}
// NewAPReq generates a new KRB_AP_REQ struct.
func NewAPReq(tkt Ticket, sessionKey types.EncryptionKey, auth types.Authenticator) (APReq, error) {
var a APReq
ed, err := encryptAuthenticator(auth, sessionKey, tkt)
if err != nil {
return a, krberror.Errorf(err, krberror.KRBMsgError, "error creating Authenticator for AP_REQ")
}
a = APReq{
PVNO: iana.PVNO,
MsgType: msgtype.KRB_AP_REQ,
APOptions: types.NewKrbFlags(),
Ticket: tkt,
Authenticator: ed,
}
return a, nil
}
// Encrypt Authenticator
func encryptAuthenticator(a types.Authenticator, sessionKey types.EncryptionKey, tkt Ticket) (types.EncryptedData, error) {
var ed types.EncryptedData
m, err := a.Marshal()
if err != nil {
return ed, krberror.Errorf(err, krberror.EncodingError, "marshaling error of EncryptedData form of Authenticator")
}
usage := authenticatorKeyUsage(tkt.SName)
ed, err = crypto.GetEncryptedData(m, sessionKey, uint32(usage), tkt.EncPart.KVNO)
if err != nil {
return ed, krberror.Errorf(err, krberror.EncryptingError, "error encrypting Authenticator")
}
return ed, nil
}
// DecryptAuthenticator decrypts the Authenticator within the AP_REQ.
// sessionKey may simply be the key within the decrypted EncPart of the ticket within the AP_REQ.
func (a *APReq) DecryptAuthenticator(sessionKey types.EncryptionKey) (auth types.Authenticator, err error) {
usage := authenticatorKeyUsage(a.Ticket.SName)
ab, e := crypto.DecryptEncPart(a.Authenticator, sessionKey, uint32(usage))
if e != nil {
err = fmt.Errorf("error decrypting authenticator: %v", e)
return
}
e = auth.Unmarshal(ab)
if e != nil {
err = fmt.Errorf("error unmarshaling authenticator")
return
}
return
}
func authenticatorKeyUsage(pn types.PrincipalName) int {
if pn.NameString[0] == "krbtgt" {
return keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR
}
return keyusage.AP_REQ_AUTHENTICATOR
}
// Unmarshal bytes b into the APReq struct.
func (a *APReq) Unmarshal(b []byte) error {
var m marshalAPReq
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.APREQ))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "unmarshal error of AP_REQ")
}
if m.MsgType != msgtype.KRB_AP_REQ {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an AP_REQ. Expected: %v; Actual: %v", msgtype.KRB_AP_REQ, m.MsgType)
}
a.PVNO = m.PVNO
a.MsgType = m.MsgType
a.APOptions = m.APOptions
a.Authenticator = m.Authenticator
a.Ticket, err = UnmarshalTicket(m.Ticket.Bytes)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "unmarshaling error of Ticket within AP_REQ")
}
return nil
}
// Marshal APReq struct.
func (a *APReq) Marshal() ([]byte, error) {
m := marshalAPReq{
PVNO: a.PVNO,
MsgType: a.MsgType,
APOptions: a.APOptions,
Authenticator: a.Authenticator,
}
var b []byte
b, err := a.Ticket.Marshal()
if err != nil {
return b, err
}
m.Ticket = asn1.RawValue{
Class: asn1.ClassContextSpecific,
IsCompound: true,
Tag: 3,
Bytes: b,
}
mk, err := asn1.Marshal(m)
if err != nil {
return mk, krberror.Errorf(err, krberror.EncodingError, "marshaling error of AP_REQ")
}
mk = asn1tools.AddASNAppTag(mk, asnAppTag.APREQ)
return mk, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/APReq_test.go 0000664 0000000 0000000 00000004202 13625372257 0024720 0 ustar 00root root 0000000 0000000 package messages
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestUnmarshalAPReq(t *testing.T) {
t.Parallel()
var a APReq
v := "encode_krb5_ap_req"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_AP_REQ, a.MsgType, "MsgType is not as expected")
assert.Equal(t, "fedcba98", hex.EncodeToString(a.APOptions.Bytes), "AP Options not as expected")
assert.Equal(t, iana.PVNO, a.Ticket.TktVNO, "Ticket VNO not as expected")
assert.Equal(t, testdata.TEST_REALM, a.Ticket.Realm, "Ticket realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Ticket.SName.NameType, "Ticket SName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.Ticket.SName.NameString), "Ticket SName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Ticket.SName.NameString, "Ticket SName name string entries not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.Ticket.EncPart.EType, "Ticket encPart etype not as expected")
assert.Equal(t, iana.PVNO, a.Ticket.EncPart.KVNO, "Ticket encPart KVNO not as expected")
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.Ticket.EncPart.Cipher, "Ticket encPart cipher not as expected")
}
func TestMarshalAPReq(t *testing.T) {
t.Parallel()
var a APReq
v := "encode_krb5_ap_req"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
mb, err := a.Marshal()
if err != nil {
t.Fatalf("Marshal of ticket errored: %v", err)
}
assert.Equal(t, b, mb, "Marshal bytes of Authenticator not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KDCRep.go 0000664 0000000 0000000 00000033555 13625372257 0023776 0 ustar 00root root 0000000 0000000 package messages
// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.4.2
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/config"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag"
"gopkg.in/jcmturner/gokrb5.v5/iana/flags"
"gopkg.in/jcmturner/gokrb5.v5/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/patype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
type marshalKDCRep struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
PAData types.PADataSequence `asn1:"explicit,optional,tag:2"`
CRealm string `asn1:"generalstring,explicit,tag:3"`
CName types.PrincipalName `asn1:"explicit,tag:4"`
// Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
Ticket asn1.RawValue `asn1:"explicit,tag:5"`
EncPart types.EncryptedData `asn1:"explicit,tag:6"`
}
// KDCRepFields represents the KRB_KDC_REP fields.
type KDCRepFields struct {
PVNO int
MsgType int
PAData []types.PAData
CRealm string
CName types.PrincipalName
Ticket Ticket
EncPart types.EncryptedData
DecryptedEncPart EncKDCRepPart
}
// ASRep implements RFC 4120 KRB_AS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2.
type ASRep struct {
KDCRepFields
}
// TGSRep implements RFC 4120 KRB_TGS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2.
type TGSRep struct {
KDCRepFields
}
// EncKDCRepPart is the encrypted part of KRB_KDC_REP.
type EncKDCRepPart struct {
Key types.EncryptionKey `asn1:"explicit,tag:0"`
LastReqs []LastReq `asn1:"explicit,tag:1"`
Nonce int `asn1:"explicit,tag:2"`
KeyExpiration time.Time `asn1:"generalized,explicit,optional,tag:3"`
Flags asn1.BitString `asn1:"explicit,tag:4"`
AuthTime time.Time `asn1:"generalized,explicit,tag:5"`
StartTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
EndTime time.Time `asn1:"generalized,explicit,tag:7"`
RenewTill time.Time `asn1:"generalized,explicit,optional,tag:8"`
SRealm string `asn1:"generalstring,explicit,tag:9"`
SName types.PrincipalName `asn1:"explicit,tag:10"`
CAddr []types.HostAddress `asn1:"explicit,optional,tag:11"`
EncPAData types.PADataSequence `asn1:"explicit,optional,tag:12"`
}
// LastReq part of KRB_KDC_REP.
type LastReq struct {
LRType int32 `asn1:"explicit,tag:0"`
LRValue time.Time `asn1:"generalized,explicit,tag:1"`
}
// Unmarshal bytes b into the ASRep struct.
func (k *ASRep) Unmarshal(b []byte) error {
var m marshalKDCRep
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREP))
if err != nil {
return processUnmarshalReplyError(b, err)
}
if m.MsgType != msgtype.KRB_AS_REP {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an AS_REP. Expected: %v; Actual: %v", msgtype.KRB_AS_REP, m.MsgType)
}
//Process the raw ticket within
tkt, err := UnmarshalTicket(m.Ticket.Bytes)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within AS_REP")
}
k.KDCRepFields = KDCRepFields{
PVNO: m.PVNO,
MsgType: m.MsgType,
PAData: m.PAData,
CRealm: m.CRealm,
CName: m.CName,
Ticket: tkt,
EncPart: m.EncPart,
}
return nil
}
// Unmarshal bytes b into the TGSRep struct.
func (k *TGSRep) Unmarshal(b []byte) error {
var m marshalKDCRep
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREP))
if err != nil {
return processUnmarshalReplyError(b, err)
}
if m.MsgType != msgtype.KRB_TGS_REP {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an TGS_REP. Expected: %v; Actual: %v", msgtype.KRB_TGS_REP, m.MsgType)
}
//Process the raw ticket within
tkt, err := UnmarshalTicket(m.Ticket.Bytes)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within TGS_REP")
}
k.KDCRepFields = KDCRepFields{
PVNO: m.PVNO,
MsgType: m.MsgType,
PAData: m.PAData,
CRealm: m.CRealm,
CName: m.CName,
Ticket: tkt,
EncPart: m.EncPart,
}
return nil
}
// Unmarshal bytes b into encrypted part of KRB_KDC_REP.
func (e *EncKDCRepPart) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncASRepPart))
if err != nil {
// Try using tag 26
_, err = asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncTGSRepPart))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part within KDC_REP")
}
}
return nil
}
// DecryptEncPart decrypts the encrypted part of an AS_REP.
func (k *ASRep) DecryptEncPart(c *credentials.Credentials) (types.EncryptionKey, error) {
var key types.EncryptionKey
var err error
if c.HasKeytab() {
key, err = c.Keytab.GetEncryptionKey(k.CName.NameString, k.CRealm, k.EncPart.KVNO, k.EncPart.EType)
if err != nil {
return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
}
}
if c.HasPassword() {
key, _, err = crypto.GetKeyFromPassword(c.Password, k.CName, k.CRealm, k.EncPart.EType, k.PAData)
if err != nil {
return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
}
}
if !c.HasKeytab() && !c.HasPassword() {
return key, krberror.NewErrorf(krberror.DecryptingError, "no secret available in credentials to preform decryption of AS_REP encrypted part")
}
b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.AS_REP_ENCPART)
if err != nil {
return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
}
var denc EncKDCRepPart
err = denc.Unmarshal(b)
if err != nil {
return key, krberror.Errorf(err, krberror.EncodingError, "error unmarshaling decrypted encpart of AS_REP")
}
k.DecryptedEncPart = denc
return key, nil
}
// IsValid checks the validity of AS_REP message.
func (k *ASRep) IsValid(cfg *config.Config, creds *credentials.Credentials, asReq ASReq) (bool, error) {
//Ref RFC 4120 Section 3.1.5
if k.CName.NameType != asReq.ReqBody.CName.NameType || k.CName.NameString == nil {
return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.CName, k.CName)
}
for i := range k.CName.NameString {
if k.CName.NameString[i] != asReq.ReqBody.CName.NameString[i] {
return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.CName, k.CName)
}
}
if k.CRealm != asReq.ReqBody.Realm {
return false, krberror.NewErrorf(krberror.KRBMsgError, "CRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.CRealm)
}
key, err := k.DecryptEncPart(creds)
if err != nil {
return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting EncPart of AS_REP")
}
if k.DecryptedEncPart.Nonce != asReq.ReqBody.Nonce {
return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request")
}
if k.DecryptedEncPart.SName.NameType != asReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil {
return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", asReq.ReqBody.SName, k.DecryptedEncPart.SName)
}
for i := range k.CName.NameString {
if k.DecryptedEncPart.SName.NameString[i] != asReq.ReqBody.SName.NameString[i] {
return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.SName, k.DecryptedEncPart.SName)
}
}
if k.DecryptedEncPart.SRealm != asReq.ReqBody.Realm {
return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.DecryptedEncPart.SRealm)
}
if len(asReq.ReqBody.Addresses) > 0 {
if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, asReq.ReqBody.Addresses) {
return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the AS_REP does not match those listed in the AS_REQ")
}
}
t := time.Now().UTC()
if t.Sub(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(t) > cfg.LibDefaults.Clockskew {
return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds", cfg.LibDefaults.Clockskew.Seconds())
}
// RFC 6806 https://tools.ietf.org/html/rfc6806.html#section-11
if asReq.PAData.Contains(patype.PA_REQ_ENC_PA_REP) && types.IsFlagSet(&k.DecryptedEncPart.Flags, flags.EncPARep) {
if len(k.DecryptedEncPart.EncPAData) < 2 || !k.DecryptedEncPart.EncPAData.Contains(patype.PA_FX_FAST) {
return false, krberror.NewErrorf(krberror.KRBMsgError, "KDC did not respond appropriately to FAST negotiation")
}
for _, pa := range k.DecryptedEncPart.EncPAData {
if pa.PADataType == patype.PA_REQ_ENC_PA_REP {
var pafast types.PAReqEncPARep
err := pafast.Unmarshal(pa.PADataValue)
if err != nil {
return false, krberror.Errorf(err, krberror.EncodingError, "KDC FAST negotiation response error, could not unmarshal PA_REQ_ENC_PA_REP")
}
etype, err := crypto.GetChksumEtype(pafast.ChksumType)
if err != nil {
return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response error")
}
ab, _ := asReq.Marshal()
if !etype.VerifyChecksum(key.KeyValue, ab, pafast.Chksum, keyusage.KEY_USAGE_AS_REQ) {
return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response checksum invalid")
}
}
}
}
return true, nil
}
// DecryptEncPart decrypts the encrypted part of an TGS_REP.
func (k *TGSRep) DecryptEncPart(key types.EncryptionKey) error {
b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.TGS_REP_ENCPART_SESSION_KEY)
if err != nil {
return krberror.Errorf(err, krberror.DecryptingError, "error decrypting TGS_REP EncPart")
}
var denc EncKDCRepPart
err = denc.Unmarshal(b)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part")
}
k.DecryptedEncPart = denc
return nil
}
// IsValid checks the validity of the TGS_REP message.
func (k *TGSRep) IsValid(cfg *config.Config, tgsReq TGSReq) (bool, error) {
if k.CName.NameType != tgsReq.ReqBody.CName.NameType || k.CName.NameString == nil {
return false, krberror.NewErrorf(krberror.KRBMsgError, "CName type in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName)
}
for i := range k.CName.NameString {
if k.CName.NameString[i] != tgsReq.ReqBody.CName.NameString[i] {
return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName)
}
}
if k.Ticket.Realm != tgsReq.ReqBody.Realm {
return false, krberror.NewErrorf(krberror.KRBMsgError, "realm in response ticket does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.Ticket.Realm)
}
if k.DecryptedEncPart.Nonce != tgsReq.ReqBody.Nonce {
return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request")
}
//if k.Ticket.SName.NameType != tgsReq.ReqBody.SName.NameType || k.Ticket.SName.NameString == nil {
// return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.Ticket.SName)
//}
//for i := range k.Ticket.SName.NameString {
// if k.Ticket.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
// return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.Ticket.SName)
// }
//}
//if k.DecryptedEncPart.SName.NameType != tgsReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil {
// return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
//}
//for i := range k.DecryptedEncPart.SName.NameString {
// if k.DecryptedEncPart.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
// return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
// }
//}
if k.DecryptedEncPart.SRealm != tgsReq.ReqBody.Realm {
return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.DecryptedEncPart.SRealm)
}
if len(k.DecryptedEncPart.CAddr) > 0 {
if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, tgsReq.ReqBody.Addresses) {
return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the TGS_REP does not match those listed in the TGS_REQ")
}
}
if time.Since(k.DecryptedEncPart.StartTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.StartTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew {
if time.Since(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew {
return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds.", cfg.LibDefaults.Clockskew.Seconds())
}
}
return true, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KDCRep_test.go 0000664 0000000 0000000 00000051501 13625372257 0025024 0 ustar 00root root 0000000 0000000 package messages
import (
"encoding/hex"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/etypeID"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/iana/patype"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
const (
testuser1EType18Keytab = "05020000004b0001000b544553542e474f4b5242350009746573747573657231000000015898e0770100120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de900000001"
testuser1EType18ASREP = "6b8202f3308202efa003020105a10302010ba22e302c302aa103020113a2230421301f301da003020112a1161b14544553542e474f4b524235746573747573657231a30d1b0b544553542e474f4b524235a4163014a003020101a10d300b1b09746573747573657231a582015a6182015630820152a003020105a10d1b0b544553542e474f4b524235a220301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a382011830820114a003020112a103020101a28201060482010237e486e32cd18ab1ac9f8d42e93f8babd7b3497084cc5599f18ec61961c6d5242d350354d99d67a7604c451116188d16cb719e84377212eac2743440e8c504ef69c755e489cc6b65f935dd032bfc076f9b2c56d816197845b8fe857d738bc59712787631a50e86833d1b0e4732c8712c856417a6a257758e7d01d3182adb3233f0dde65d228c240ed26aa1af69f8d765dc0bc69096fdb037a75af220fea176839528d44b70f7dabfaa2ea506de1296f847176a60c501fd8cef8e0a51399bb6d5f753962d96292e93ffe344c6630db912931d46d88c0279f00719e22d0efcfd4ee33a702d0b660c1f13970a9beec12c0c8af3dda68bd81ac1fe3f126d2a24ebb445c5a682012c30820128a003020112a282011f0482011bb149cc16018072c4c18788d95a33aba540e52c11b54a93e67e788d05de75d8f3d4aa1afafbbfa6fde3eb40e5aa1890644cea2607efd5213a3fd00345b02eeb9ae1b589f36c74c689cd4ec1239dfe61e42ba6afa33f6240e3cfab291e4abb465d273302dbf7dbd148a299a9369044dd03377c1687e7dd36aa66501284a4ca50c0a7b08f4f87aecfa23b0dd0b11490e3ad330906dab715de81fc52f120d09c39990b8b5330d4601cc396b2ed258834329c4cc02c563a12de3ef9bf11e946258bc2ab5257f4caa4d443a7daf0fc25f6f531c2fcba88af8ca55c85300997cd05abbea52811fe2d038ba8f62fc8e3bc71ce04362d356ea2e1df8ac55c784c53cfb07817d48e39fe99fc8788040d98209c79dcf044d97e80de9f47824646"
testRealm = "TEST.GOKRB5"
testUser = "testuser1"
testUserPassword = "passwordvalue"
)
func TestUnmarshalASRep(t *testing.T) {
t.Parallel()
var a ASRep
v := "encode_krb5_as_rep"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_AS_REP, a.MsgType, "MsgType not as expected")
assert.Equal(t, 2, len(a.PAData), "Number of PAData items in the sequence not as expected")
for i, pa := range a.PAData {
assert.Equal(t, patype.PA_SAM_RESPONSE, pa.PADataType, fmt.Sprintf("PAData type for entry %d not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_PADATA_VALUE), pa.PADataValue, fmt.Sprintf("PAData valye for entry %d not as expected", i+1))
}
assert.Equal(t, testdata.TEST_REALM, a.CRealm, "Client Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected")
assert.Equal(t, iana.PVNO, a.Ticket.TktVNO, "TktVNO not as expected")
assert.Equal(t, testdata.TEST_REALM, a.Ticket.Realm, "Ticket Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Ticket.SName.NameType, "Ticket service nametype not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.Ticket.SName.NameString), "SName in ticket does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Ticket.SName.NameString, "Ticket SName entries not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected")
assert.Equal(t, iana.PVNO, a.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected")
assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.Ticket.EncPart.Cipher), "Ticket encrypted part cipher not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Etype of encrypted part not as expected")
assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "Encrypted part KVNO not as expected")
assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.EncPart.Cipher), "Ticket encrypted part cipher not as expected")
}
func TestUnmarshalASRep_optionalsNULL(t *testing.T) {
t.Parallel()
var a ASRep
v := "encode_krb5_as_rep(optionalsNULL)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_AS_REP, a.MsgType, "MsgType not as expected")
assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected")
assert.Equal(t, testdata.TEST_REALM, a.CRealm, "Client Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected")
assert.Equal(t, iana.PVNO, a.Ticket.TktVNO, "TktVNO not as expected")
assert.Equal(t, testdata.TEST_REALM, a.Ticket.Realm, "Ticket Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Ticket.SName.NameType, "Ticket service nametype not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.Ticket.SName.NameString), "SName in ticket does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Ticket.SName.NameString, "Ticket SName entries not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected")
assert.Equal(t, iana.PVNO, a.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected")
assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.Ticket.EncPart.Cipher), "Ticket encrypted part cipher not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Etype of encrypted part not as expected")
assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "Encrypted part KVNO not as expected")
assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.EncPart.Cipher), "Ticket encrypted part cipher not as expected")
}
func TestUnmarshalTGSRep(t *testing.T) {
t.Parallel()
var a TGSRep
v := "encode_krb5_tgs_rep"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_TGS_REP, a.MsgType, "MsgType not as expected")
assert.Equal(t, 2, len(a.PAData), "Number of PAData items in the sequence not as expected")
for i, pa := range a.PAData {
assert.Equal(t, patype.PA_SAM_RESPONSE, pa.PADataType, fmt.Sprintf("PAData type for entry %d not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_PADATA_VALUE), pa.PADataValue, fmt.Sprintf("PAData valye for entry %d not as expected", i+1))
}
assert.Equal(t, testdata.TEST_REALM, a.CRealm, "Client Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected")
assert.Equal(t, iana.PVNO, a.Ticket.TktVNO, "TktVNO not as expected")
assert.Equal(t, testdata.TEST_REALM, a.Ticket.Realm, "Ticket Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Ticket.SName.NameType, "Ticket service nametype not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.Ticket.SName.NameString), "SName in ticket does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Ticket.SName.NameString, "Ticket SName entries not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected")
assert.Equal(t, iana.PVNO, a.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected")
assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.Ticket.EncPart.Cipher), "Ticket encrypted part cipher not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Etype of encrypted part not as expected")
assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "Encrypted part KVNO not as expected")
assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.EncPart.Cipher), "Ticket encrypted part cipher not as expected")
}
func TestUnmarshalTGSRep_optionalsNULL(t *testing.T) {
t.Parallel()
var a TGSRep
v := "encode_krb5_tgs_rep(optionalsNULL)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_TGS_REP, a.MsgType, "MsgType not as expected")
assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected")
assert.Equal(t, testdata.TEST_REALM, a.CRealm, "Client Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected")
assert.Equal(t, iana.PVNO, a.Ticket.TktVNO, "TktVNO not as expected")
assert.Equal(t, testdata.TEST_REALM, a.Ticket.Realm, "Ticket Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Ticket.SName.NameType, "Ticket service nametype not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.Ticket.SName.NameString), "SName in ticket does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Ticket.SName.NameString, "Ticket SName entries not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected")
assert.Equal(t, iana.PVNO, a.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected")
assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.Ticket.EncPart.Cipher), "Ticket encrypted part cipher not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Etype of encrypted part not as expected")
assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "Encrypted part KVNO not as expected")
assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.EncPart.Cipher), "Ticket encrypted part cipher not as expected")
}
func TestUnmarshalEncKDCRepPart(t *testing.T) {
t.Parallel()
var a EncKDCRepPart
v := "encode_krb5_enc_kdc_rep_part"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, int32(1), a.Key.KeyType, "Key type not as expected")
assert.Equal(t, []byte("12345678"), a.Key.KeyValue, "Key value not as expected")
assert.Equal(t, 2, len(a.LastReqs), "Number of last request entries not as expected")
for i, r := range a.LastReqs {
assert.Equal(t, int32(-5), r.LRType, fmt.Sprintf("Last request typ not as expected for last request entry %d", i+1))
assert.Equal(t, tt, r.LRValue, fmt.Sprintf("Last request time value not as expected for last request entry %d", i+1))
}
assert.Equal(t, testdata.TEST_NONCE, a.Nonce, "Nonce not as expected")
assert.Equal(t, tt, a.KeyExpiration, "key expiration time not as expected")
assert.Equal(t, "fedcba98", hex.EncodeToString(a.Flags.Bytes), "Flags not as expected")
assert.Equal(t, tt, a.AuthTime, "Auth time not as expected")
assert.Equal(t, tt, a.StartTime, "Start time not as expected")
assert.Equal(t, tt, a.EndTime, "End time not as expected")
assert.Equal(t, tt, a.RenewTill, "Renew Till time not as expected")
assert.Equal(t, testdata.TEST_REALM, a.SRealm, "SRealm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "SName type not as expected")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "SName string entries not as expected")
assert.Equal(t, 2, len(a.CAddr), "Number of client addresses not as expected")
for i, addr := range a.CAddr {
assert.Equal(t, int32(2), addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d", i+1))
assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d", i+1))
}
}
func TestUnmarshalEncKDCRepPart_optionalsNULL(t *testing.T) {
t.Parallel()
var a EncKDCRepPart
v := "encode_krb5_enc_kdc_rep_part(optionalsNULL)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, int32(1), a.Key.KeyType, "Key type not as expected")
assert.Equal(t, []byte("12345678"), a.Key.KeyValue, "Key value not as expected")
assert.Equal(t, 2, len(a.LastReqs), "Number of last request entries not as expected")
for i, r := range a.LastReqs {
assert.Equal(t, int32(-5), r.LRType, fmt.Sprintf("Last request typ not as expected for last request entry %d", i+1))
assert.Equal(t, tt, r.LRValue, fmt.Sprintf("Last request time value not as expected for last request entry %d", i+1))
}
assert.Equal(t, testdata.TEST_NONCE, a.Nonce, "Nonce not as expected")
assert.Equal(t, "fe5cba98", hex.EncodeToString(a.Flags.Bytes), "Flags not as expected")
assert.Equal(t, tt, a.AuthTime, "Auth time not as expected")
assert.Equal(t, tt, a.EndTime, "End time not as expected")
assert.Equal(t, testdata.TEST_REALM, a.SRealm, "SRealm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "SName type not as expected")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "SName string entries not as expected")
}
func TestUnmarshalASRepDecodeAndDecrypt(t *testing.T) {
t.Parallel()
var asRep ASRep
b, _ := hex.DecodeString(testuser1EType18ASREP)
err := asRep.Unmarshal(b)
if err != nil {
t.Fatalf("AS REP Unmarshal error: %v\n", err)
}
assert.Equal(t, 5, asRep.PVNO, "PVNO not as expected")
assert.Equal(t, 11, asRep.MsgType, "MsgType not as expected")
assert.Equal(t, testRealm, asRep.CRealm, "Client Realm not as expected")
assert.Equal(t, int32(1), asRep.CName.NameType, "CName NameType not as expected")
assert.Equal(t, testUser, asRep.CName.NameString[0], "CName NameType not as expected")
assert.Equal(t, int32(19), asRep.PAData[0].PADataType, "PADataType not as expected")
assert.Equal(t, 5, asRep.Ticket.TktVNO, "TktVNO not as expected")
assert.Equal(t, testRealm, asRep.Ticket.Realm, "Ticket Realm not as expected")
assert.Equal(t, int32(2), asRep.Ticket.SName.NameType, "Ticket service nametype not as expected")
assert.Equal(t, "krbtgt", asRep.Ticket.SName.NameString[0], "Ticket service name string not as expected")
assert.Equal(t, testRealm, asRep.Ticket.SName.NameString[1], "Ticket service name string not as expected")
assert.Equal(t, etypeID.ETypesByName["aes256-cts-hmac-sha1-96"], asRep.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected")
assert.Equal(t, 1, asRep.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected")
assert.Equal(t, etypeID.ETypesByName["aes256-cts-hmac-sha1-96"], asRep.EncPart.EType, "Etype of encrypted part not as expected")
assert.Equal(t, 0, asRep.EncPart.KVNO, "Encrypted part KVNO not as expected")
//t.Log("Finished testing unecrypted parts of AS REP")
ktb, _ := hex.DecodeString(testuser1EType18Keytab)
kt, err := keytab.Parse(ktb)
if err != nil {
t.Fatalf("keytab parse error: %v\n", err)
}
cred := credentials.NewCredentials(testUser, testRealm)
_, err = asRep.DecryptEncPart(cred.WithKeytab(kt))
if err != nil {
t.Fatalf("Decryption of AS_REP EncPart failed: %v", err)
}
assert.Equal(t, int32(18), asRep.DecryptedEncPart.Key.KeyType, "KeyType in decrypted EncPart not as expected")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.LastReqs[0].LRValue, "LastReqs did not have a time value")
assert.Equal(t, 2069991465, asRep.DecryptedEncPart.Nonce, "Nonce value not as expected")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.KeyExpiration, "Key expiration not a time type")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.AuthTime, "AuthTime not a time type")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.StartTime, "StartTime not a time type")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.EndTime, "StartTime not a time type")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.RenewTill, "RenewTill not a time type")
assert.Equal(t, testRealm, asRep.DecryptedEncPart.SRealm, "Service realm not as expected")
assert.Equal(t, int32(2), asRep.DecryptedEncPart.SName.NameType, "Name type for AS_REP not as expected")
assert.Equal(t, []string{"krbtgt", testRealm}, asRep.DecryptedEncPart.SName.NameString, "Service name string not as expected")
}
func TestUnmarshalASRepDecodeAndDecrypt_withPassword(t *testing.T) {
t.Parallel()
var asRep ASRep
b, _ := hex.DecodeString(testuser1EType18ASREP)
err := asRep.Unmarshal(b)
if err != nil {
t.Fatalf("AS REP Unmarshal error: %v\n", err)
}
assert.Equal(t, 5, asRep.PVNO, "PVNO not as expected")
assert.Equal(t, 11, asRep.MsgType, "MsgType not as expected")
assert.Equal(t, testRealm, asRep.CRealm, "Client Realm not as expected")
assert.Equal(t, int32(1), asRep.CName.NameType, "CName NameType not as expected")
assert.Equal(t, testUser, asRep.CName.NameString[0], "CName NameType not as expected")
assert.Equal(t, int32(19), asRep.PAData[0].PADataType, "PADataType not as expected")
assert.Equal(t, 5, asRep.Ticket.TktVNO, "TktVNO not as expected")
assert.Equal(t, testRealm, asRep.Ticket.Realm, "Ticket Realm not as expected")
assert.Equal(t, int32(2), asRep.Ticket.SName.NameType, "Ticket service nametype not as expected")
assert.Equal(t, "krbtgt", asRep.Ticket.SName.NameString[0], "Ticket service name string not as expected")
assert.Equal(t, testRealm, asRep.Ticket.SName.NameString[1], "Ticket service name string not as expected")
assert.Equal(t, etypeID.AES256_CTS_HMAC_SHA1_96, asRep.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected")
assert.Equal(t, 1, asRep.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected")
assert.Equal(t, etypeID.AES256_CTS_HMAC_SHA1_96, asRep.EncPart.EType, "Etype of encrypted part not as expected")
assert.Equal(t, 0, asRep.EncPart.KVNO, "Encrypted part KVNO not as expected")
cred := credentials.NewCredentials(testUser, testRealm)
_, err = asRep.DecryptEncPart(cred.WithPassword(testUserPassword))
if err != nil {
t.Fatalf("Decryption of AS_REP EncPart failed: %v", err)
}
assert.Equal(t, int32(18), asRep.DecryptedEncPart.Key.KeyType, "KeyType in decrypted EncPart not as expected")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.LastReqs[0].LRValue, "LastReqs did not have a time value")
assert.Equal(t, 2069991465, asRep.DecryptedEncPart.Nonce, "Nonce value not as expected")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.KeyExpiration, "Key expiration not a time type")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.AuthTime, "AuthTime not a time type")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.StartTime, "StartTime not a time type")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.EndTime, "StartTime not a time type")
assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.RenewTill, "RenewTill not a time type")
assert.Equal(t, testRealm, asRep.DecryptedEncPart.SRealm, "Service realm not as expected")
assert.Equal(t, nametype.KRB_NT_SRV_INST, asRep.DecryptedEncPart.SName.NameType, "Name type for AS_REP not as expected")
assert.Equal(t, []string{"krbtgt", testRealm}, asRep.DecryptedEncPart.SName.NameString, "Service name string not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KDCReq.go 0000664 0000000 0000000 00000031474 13625372257 0023775 0 ustar 00root root 0000000 0000000 package messages
// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.4.1
import (
"crypto/rand"
"fmt"
"math"
"math/big"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/asn1tools"
"gopkg.in/jcmturner/gokrb5.v5/config"
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag"
"gopkg.in/jcmturner/gokrb5.v5/iana/flags"
"gopkg.in/jcmturner/gokrb5.v5/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/iana/patype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
type marshalKDCReq struct {
PVNO int `asn1:"explicit,tag:1"`
MsgType int `asn1:"explicit,tag:2"`
PAData types.PADataSequence `asn1:"explicit,optional,tag:3"`
ReqBody asn1.RawValue `asn1:"explicit,tag:4"`
}
// KDCReqFields represents the KRB_KDC_REQ fields.
type KDCReqFields struct {
PVNO int
MsgType int
PAData types.PADataSequence
ReqBody KDCReqBody
Renewal bool
}
// ASReq implements RFC 4120 KRB_AS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1.
type ASReq struct {
KDCReqFields
}
// TGSReq implements RFC 4120 KRB_TGS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1.
type TGSReq struct {
KDCReqFields
}
type marshalKDCReqBody struct {
KDCOptions asn1.BitString `asn1:"explicit,tag:0"`
CName types.PrincipalName `asn1:"explicit,optional,tag:1"`
Realm string `asn1:"generalstring,explicit,tag:2"`
SName types.PrincipalName `asn1:"explicit,optional,tag:3"`
From time.Time `asn1:"generalized,explicit,optional,tag:4"`
Till time.Time `asn1:"generalized,explicit,tag:5"`
RTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
Nonce int `asn1:"explicit,tag:7"`
EType []int32 `asn1:"explicit,tag:8"`
Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"`
EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"`
// Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
AdditionalTickets asn1.RawValue `asn1:"explicit,optional,tag:11"`
}
// KDCReqBody implements the KRB_KDC_REQ request body.
type KDCReqBody struct {
KDCOptions asn1.BitString `asn1:"explicit,tag:0"`
CName types.PrincipalName `asn1:"explicit,optional,tag:1"`
Realm string `asn1:"generalstring,explicit,tag:2"`
SName types.PrincipalName `asn1:"explicit,optional,tag:3"`
From time.Time `asn1:"generalized,explicit,optional,tag:4"`
Till time.Time `asn1:"generalized,explicit,tag:5"`
RTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
Nonce int `asn1:"explicit,tag:7"`
EType []int32 `asn1:"explicit,tag:8"`
Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"`
EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"`
AdditionalTickets []Ticket `asn1:"explicit,optional,tag:11"`
}
// NewASReqForTGT generates a new KRB_AS_REQ struct for a TGT request.
func NewASReqForTGT(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) {
sname := types.PrincipalName{
NameType: nametype.KRB_NT_SRV_INST,
NameString: []string{"krbtgt", realm},
}
return NewASReq(realm, c, cname, sname)
}
// NewASReqForChgPasswd generates a new KRB_AS_REQ struct for a change password request.
func NewASReqForChgPasswd(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) {
sname := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"kadmin", "changepw"},
}
return NewASReq(realm, c, cname, sname)
}
// NewASReq generates a new KRB_AS_REQ struct for a given SNAME.
func NewASReq(realm string, c *config.Config, cname, sname types.PrincipalName) (ASReq, error) {
nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
if err != nil {
return ASReq{}, err
}
t := time.Now().UTC()
// Copy the default options to make this thread safe
kopts := types.NewKrbFlags()
copy(kopts.Bytes, c.LibDefaults.KDCDefaultOptions.Bytes)
kopts.BitLength = c.LibDefaults.KDCDefaultOptions.BitLength
a := ASReq{
KDCReqFields{
PVNO: iana.PVNO,
MsgType: msgtype.KRB_AS_REQ,
PAData: types.PADataSequence{},
ReqBody: KDCReqBody{
KDCOptions: kopts,
Realm: realm,
CName: cname,
SName: sname,
Till: t.Add(c.LibDefaults.TicketLifetime),
Nonce: int(nonce.Int64()),
EType: c.LibDefaults.DefaultTktEnctypeIDs,
},
},
}
if c.LibDefaults.Forwardable {
types.SetFlag(&a.ReqBody.KDCOptions, flags.Forwardable)
}
if c.LibDefaults.Canonicalize {
types.SetFlag(&a.ReqBody.KDCOptions, flags.Canonicalize)
}
if c.LibDefaults.Proxiable {
types.SetFlag(&a.ReqBody.KDCOptions, flags.Proxiable)
}
if c.LibDefaults.RenewLifetime != 0 {
types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable)
a.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime)
a.ReqBody.RTime = t.Add(time.Duration(48) * time.Hour)
}
if !c.LibDefaults.NoAddresses {
ha, err := types.LocalHostAddresses()
if err != nil {
return a, fmt.Errorf("could not get local addresses: %v", err)
}
ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...)
a.ReqBody.Addresses = ha
}
return a, nil
}
// NewTGSReq generates a new KRB_TGS_REQ struct.
func NewTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, tkt Ticket, sessionKey types.EncryptionKey, spn types.PrincipalName, renewal bool) (TGSReq, error) {
nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
if err != nil {
return TGSReq{}, err
}
t := time.Now().UTC()
a := TGSReq{
KDCReqFields{
PVNO: iana.PVNO,
MsgType: msgtype.KRB_TGS_REQ,
ReqBody: KDCReqBody{
KDCOptions: types.NewKrbFlags(),
Realm: kdcRealm,
SName: spn,
Till: t.Add(c.LibDefaults.TicketLifetime),
Nonce: int(nonce.Int64()),
EType: c.LibDefaults.DefaultTGSEnctypeIDs,
},
Renewal: renewal,
},
}
if c.LibDefaults.Forwardable {
types.SetFlag(&a.ReqBody.KDCOptions, flags.Forwardable)
}
if c.LibDefaults.Canonicalize {
types.SetFlag(&a.ReqBody.KDCOptions, flags.Canonicalize)
}
if c.LibDefaults.Proxiable {
types.SetFlag(&a.ReqBody.KDCOptions, flags.Proxiable)
}
if c.LibDefaults.RenewLifetime > time.Duration(0) {
types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable)
a.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime)
}
if !c.LibDefaults.NoAddresses {
ha, err := types.LocalHostAddresses()
if err != nil {
return a, fmt.Errorf("could not get local addresses: %v", err)
}
ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...)
a.ReqBody.Addresses = ha
}
if renewal {
types.SetFlag(&a.ReqBody.KDCOptions, flags.Renew)
types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable)
}
auth, err := types.NewAuthenticator(tkt.Realm, cname)
if err != nil {
return a, krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
}
// Add the CName to make validation of the reply easier
a.ReqBody.CName = auth.CName
b, err := a.ReqBody.Marshal()
if err != nil {
return a, krberror.Errorf(err, krberror.EncodingError, "error marshaling TGS_REQ body")
}
etype, err := crypto.GetEtype(sessionKey.KeyType)
if err != nil {
return a, krberror.Errorf(err, krberror.EncryptingError, "error getting etype to encrypt authenticator")
}
cb, err := etype.GetChecksumHash(sessionKey.KeyValue, b, keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM)
if err != nil {
return a, krberror.Errorf(err, krberror.ChksumError, "error getting etype checksum hash")
}
auth.Cksum = types.Checksum{
CksumType: etype.GetHashID(),
Checksum: cb,
}
apReq, err := NewAPReq(tkt, sessionKey, auth)
if err != nil {
return a, krberror.Errorf(err, krberror.KRBMsgError, "error generating new AP_REQ")
}
apb, err := apReq.Marshal()
if err != nil {
return a, krberror.Errorf(err, krberror.EncodingError, "error marshaling AP_REQ for pre-authentication data")
}
a.PAData = types.PADataSequence{
types.PAData{
PADataType: patype.PA_TGS_REQ,
PADataValue: apb,
},
}
return a, nil
}
// Unmarshal bytes b into the ASReq struct.
func (k *ASReq) Unmarshal(b []byte) error {
var m marshalKDCReq
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREQ))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling AS_REQ")
}
expectedMsgType := msgtype.KRB_AS_REQ
if m.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a AS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
}
var reqb KDCReqBody
err = reqb.Unmarshal(m.ReqBody.Bytes)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error processing AS_REQ body")
}
k.MsgType = m.MsgType
k.PAData = m.PAData
k.PVNO = m.PVNO
k.ReqBody = reqb
return nil
}
// Unmarshal bytes b into the TGSReq struct.
func (k *TGSReq) Unmarshal(b []byte) error {
var m marshalKDCReq
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREQ))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling TGS_REQ")
}
expectedMsgType := msgtype.KRB_TGS_REQ
if m.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a TGS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
}
var reqb KDCReqBody
err = reqb.Unmarshal(m.ReqBody.Bytes)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error processing TGS_REQ body")
}
k.MsgType = m.MsgType
k.PAData = m.PAData
k.PVNO = m.PVNO
k.ReqBody = reqb
return nil
}
// Unmarshal bytes b into the KRB_KDC_REQ body struct.
func (k *KDCReqBody) Unmarshal(b []byte) error {
var m marshalKDCReqBody
_, err := asn1.Unmarshal(b, &m)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling KDC_REQ body")
}
k.KDCOptions = m.KDCOptions
if len(k.KDCOptions.Bytes) < 4 {
tb := make([]byte, 4-len(k.KDCOptions.Bytes))
k.KDCOptions.Bytes = append(tb, k.KDCOptions.Bytes...)
k.KDCOptions.BitLength = len(k.KDCOptions.Bytes) * 8
}
k.CName = m.CName
k.Realm = m.Realm
k.SName = m.SName
k.From = m.From
k.Till = m.Till
k.RTime = m.RTime
k.Nonce = m.Nonce
k.EType = m.EType
k.Addresses = m.Addresses
k.EncAuthData = m.EncAuthData
if len(m.AdditionalTickets.Bytes) > 0 {
k.AdditionalTickets, err = UnmarshalTicketsSequence(m.AdditionalTickets)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling additional tickets")
}
}
return nil
}
// Marshal ASReq struct.
func (k *ASReq) Marshal() ([]byte, error) {
m := marshalKDCReq{
PVNO: k.PVNO,
MsgType: k.MsgType,
PAData: k.PAData,
}
b, err := k.ReqBody.Marshal()
if err != nil {
var mk []byte
return mk, err
}
m.ReqBody = asn1.RawValue{
Class: asn1.ClassContextSpecific,
IsCompound: true,
Tag: 4,
Bytes: b,
}
mk, err := asn1.Marshal(m)
if err != nil {
return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ")
}
mk = asn1tools.AddASNAppTag(mk, asnAppTag.ASREQ)
return mk, nil
}
// Marshal TGSReq struct.
func (k *TGSReq) Marshal() ([]byte, error) {
m := marshalKDCReq{
PVNO: k.PVNO,
MsgType: k.MsgType,
PAData: k.PAData,
}
b, err := k.ReqBody.Marshal()
if err != nil {
var mk []byte
return mk, err
}
m.ReqBody = asn1.RawValue{
Class: asn1.ClassContextSpecific,
IsCompound: true,
Tag: 4,
Bytes: b,
}
mk, err := asn1.Marshal(m)
if err != nil {
return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ")
}
mk = asn1tools.AddASNAppTag(mk, asnAppTag.TGSREQ)
return mk, nil
}
// Marshal KRB_KDC_REQ body struct.
func (k *KDCReqBody) Marshal() ([]byte, error) {
var b []byte
m := marshalKDCReqBody{
KDCOptions: k.KDCOptions,
CName: k.CName,
Realm: k.Realm,
SName: k.SName,
From: k.From,
Till: k.Till,
RTime: k.RTime,
Nonce: k.Nonce,
EType: k.EType,
Addresses: k.Addresses,
EncAuthData: k.EncAuthData,
}
rawtkts, err := MarshalTicketSequence(k.AdditionalTickets)
if err != nil {
return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body additional tickets")
}
//The asn1.rawValue needs the tag setting on it for where it is in the KDCReqBody
rawtkts.Tag = 11
if len(rawtkts.Bytes) > 0 {
m.AdditionalTickets = rawtkts
}
b, err = asn1.Marshal(m)
if err != nil {
return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body")
}
return b, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KDCReq_test.go 0000664 0000000 0000000 00000066415 13625372257 0025037 0 ustar 00root root 0000000 0000000 package messages
import (
"encoding/hex"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/addrtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/iana/patype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestUnmarshalKDCReqBody(t *testing.T) {
t.Parallel()
var a KDCReqBody
v := "encode_krb5_kdc_req_body"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, "fedcba90", hex.EncodeToString(a.KDCOptions.Bytes), "Request body flags not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "Request body CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "Request body CName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "Request body CName entries not as expected")
assert.Equal(t, testdata.TEST_REALM, a.Realm, "Request body Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "Request body SName nametype not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.SName.NameString), "Request body SName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "Request body SName entries not as expected")
assert.Equal(t, tt, a.From, "Request body From time not as expected")
assert.Equal(t, tt, a.Till, "Request body Till time not as expected")
assert.Equal(t, tt, a.RTime, "Request body RTime time not as expected")
assert.Equal(t, testdata.TEST_NONCE, a.Nonce, "Request body nounce not as expected")
assert.Equal(t, []int32{0, 1}, a.EType, "Etype list not as expected")
assert.Equal(t, 2, len(a.Addresses), "Number of client addresses not as expected")
for i, addr := range a.Addresses {
assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d", i+1))
assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d", i+1))
}
assert.Equal(t, testdata.TEST_ETYPE, a.EncAuthData.EType, "Etype of request body encrypted authorization data not as expected")
assert.Equal(t, iana.PVNO, a.EncAuthData.KVNO, "KVNO of request body encrypted authorization data not as expected")
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.EncAuthData.Cipher, "Ciphertext of request body encrypted authorization data not as expected")
assert.Equal(t, 2, len(a.AdditionalTickets), "Number of additional tickets not as expected")
for i, tkt := range a.AdditionalTickets {
assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1))
assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1))
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1))
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1))
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1))
assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1))
assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1))
}
}
func TestUnmarshalKDCReqBody_optionalsNULLexceptsecond_ticket(t *testing.T) {
t.Parallel()
var a KDCReqBody
v := "encode_krb5_kdc_req_body(optionalsNULLexceptsecond_ticket)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, "fedcba98", hex.EncodeToString(a.KDCOptions.Bytes), "Request body flags not as expected")
assert.Equal(t, testdata.TEST_REALM, a.Realm, "Request body Realm not as expected")
assert.Equal(t, tt, a.Till, "Request body Till time not as expected")
assert.Equal(t, testdata.TEST_NONCE, a.Nonce, "Request body nounce not as expected")
assert.Equal(t, []int32{0, 1}, a.EType, "Etype list not as expected")
assert.Equal(t, 0, len(a.Addresses), "Number of client addresses not empty")
assert.Equal(t, 0, len(a.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty")
assert.Equal(t, 2, len(a.AdditionalTickets), "Number of additional tickets not as expected")
for i, tkt := range a.AdditionalTickets {
assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1))
assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1))
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1))
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1))
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1))
assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1))
assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1))
}
}
func TestUnmarshalKDCReqBody_optionalsNULLexceptserver(t *testing.T) {
t.Parallel()
var a KDCReqBody
v := "encode_krb5_kdc_req_body(optionalsNULLexceptserver)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, "fedcba90", hex.EncodeToString(a.KDCOptions.Bytes), "Request body flags not as expected")
assert.Equal(t, testdata.TEST_REALM, a.Realm, "Request body Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "Request body SName nametype not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.SName.NameString), "Request body SName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "Request body SName entries not as expected")
assert.Equal(t, tt, a.Till, "Request body Till time not as expected")
assert.Equal(t, testdata.TEST_NONCE, a.Nonce, "Request body nounce not as expected")
assert.Equal(t, []int32{0, 1}, a.EType, "Etype list not as expected")
assert.Equal(t, 0, len(a.Addresses), "Number of client addresses not empty")
assert.Equal(t, 0, len(a.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty")
assert.Equal(t, 0, len(a.AdditionalTickets), "Number of additional tickets not empty")
}
func TestUnmarshalASReq(t *testing.T) {
t.Parallel()
var a ASReq
v := "encode_krb5_as_req"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_AS_REQ, a.MsgType, "Message ID not as expected")
assert.Equal(t, 2, len(a.PAData), "Number of PAData items in the sequence not as expected")
for i, pa := range a.PAData {
assert.Equal(t, patype.PA_SAM_RESPONSE, pa.PADataType, fmt.Sprintf("PAData type for entry %d not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_PADATA_VALUE), pa.PADataValue, fmt.Sprintf("PAData valye for entry %d not as expected", i+1))
}
assert.Equal(t, "fedcba90", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.CName.NameType, "Request body CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.CName.NameString), "Request body CName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.CName.NameString, "Request body CName entries not as expected")
assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.SName.NameType, "Request body SName nametype not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.SName.NameString), "Request body SName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.SName.NameString, "Request body SName entries not as expected")
assert.Equal(t, tt, a.ReqBody.From, "Request body From time not as expected")
assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected")
assert.Equal(t, tt, a.ReqBody.RTime, "Request body RTime time not as expected")
assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected")
assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected")
assert.Equal(t, 2, len(a.ReqBody.Addresses), "Number of client addresses not as expected")
for i, addr := range a.ReqBody.Addresses {
assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d", i+1))
assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d", i+1))
}
assert.Equal(t, testdata.TEST_ETYPE, a.ReqBody.EncAuthData.EType, "Etype of request body encrypted authorization data not as expected")
assert.Equal(t, iana.PVNO, a.ReqBody.EncAuthData.KVNO, "KVNO of request body encrypted authorization data not as expected")
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.ReqBody.EncAuthData.Cipher, "Ciphertext of request body encrypted authorization data not as expected")
assert.Equal(t, 2, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not as expected")
for i, tkt := range a.ReqBody.AdditionalTickets {
assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1))
assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1))
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1))
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1))
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1))
assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1))
assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1))
}
}
func TestUnmarshalASReq_optionalsNULLexceptsecond_ticket(t *testing.T) {
t.Parallel()
var a ASReq
v := "encode_krb5_as_req(optionalsNULLexceptsecond_ticket)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_AS_REQ, a.MsgType, "Message ID not as expected")
assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected")
assert.Equal(t, "fedcba98", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected")
assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected")
assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected")
assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected")
assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected")
assert.Equal(t, 0, len(a.ReqBody.Addresses), "Number of client addresses not empty")
assert.Equal(t, 0, len(a.ReqBody.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty")
assert.Equal(t, 2, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not as expected")
for i, tkt := range a.ReqBody.AdditionalTickets {
assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1))
assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1))
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1))
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1))
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1))
assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1))
assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1))
}
}
func TestUnmarshalASReq_optionalsNULLexceptserver(t *testing.T) {
t.Parallel()
var a ASReq
v := "encode_krb5_as_req(optionalsNULLexceptserver)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_AS_REQ, a.MsgType, "Message ID not as expected")
assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected")
assert.Equal(t, "fedcba90", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected")
assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.SName.NameType, "Request body SName nametype not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.SName.NameString), "Request body SName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.SName.NameString, "Request body SName entries not as expected")
assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected")
assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected")
assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected")
assert.Equal(t, 0, len(a.ReqBody.Addresses), "Number of client addresses not empty")
assert.Equal(t, 0, len(a.ReqBody.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty")
assert.Equal(t, 0, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not empty")
}
func TestUnmarshalTGSReq(t *testing.T) {
t.Parallel()
var a TGSReq
v := "encode_krb5_tgs_req"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_TGS_REQ, a.MsgType, "Message ID not as expected")
assert.Equal(t, 2, len(a.PAData), "Number of PAData items in the sequence not as expected")
for i, pa := range a.PAData {
assert.Equal(t, patype.PA_SAM_RESPONSE, pa.PADataType, fmt.Sprintf("PAData type for entry %d not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_PADATA_VALUE), pa.PADataValue, fmt.Sprintf("PAData valye for entry %d not as expected", i+1))
}
assert.Equal(t, "fedcba90", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.CName.NameType, "Request body CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.CName.NameString), "Request body CName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.CName.NameString, "Request body CName entries not as expected")
assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.SName.NameType, "Request body SName nametype not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.SName.NameString), "Request body SName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.SName.NameString, "Request body SName entries not as expected")
assert.Equal(t, tt, a.ReqBody.From, "Request body From time not as expected")
assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected")
assert.Equal(t, tt, a.ReqBody.RTime, "Request body RTime time not as expected")
assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected")
assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected")
assert.Equal(t, 2, len(a.ReqBody.Addresses), "Number of client addresses not as expected")
for i, addr := range a.ReqBody.Addresses {
assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d", i+1))
assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d", i+1))
}
assert.Equal(t, testdata.TEST_ETYPE, a.ReqBody.EncAuthData.EType, "Etype of request body encrypted authorization data not as expected")
assert.Equal(t, iana.PVNO, a.ReqBody.EncAuthData.KVNO, "KVNO of request body encrypted authorization data not as expected")
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.ReqBody.EncAuthData.Cipher, "Ciphertext of request body encrypted authorization data not as expected")
assert.Equal(t, 2, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not as expected")
for i, tkt := range a.ReqBody.AdditionalTickets {
assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1))
assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1))
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1))
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1))
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1))
assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1))
assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1))
}
}
func TestUnmarshalTGSReq_optionalsNULLexceptsecond_ticket(t *testing.T) {
t.Parallel()
var a TGSReq
v := "encode_krb5_tgs_req(optionalsNULLexceptsecond_ticket)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_TGS_REQ, a.MsgType, "Message ID not as expected")
assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected")
assert.Equal(t, "fedcba98", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected")
assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected")
assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected")
assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected")
assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected")
assert.Equal(t, 0, len(a.ReqBody.Addresses), "Number of client addresses not empty")
assert.Equal(t, 0, len(a.ReqBody.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty")
assert.Equal(t, 2, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not as expected")
for i, tkt := range a.ReqBody.AdditionalTickets {
assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1))
assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1))
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1))
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1))
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1))
assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1))
assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1))
}
}
func TestUnmarshalTGSReq_optionalsNULLexceptserver(t *testing.T) {
t.Parallel()
var a TGSReq
v := "encode_krb5_tgs_req(optionalsNULLexceptserver)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_TGS_REQ, a.MsgType, "Message ID not as expected")
assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected")
assert.Equal(t, "fedcba90", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected")
assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.SName.NameType, "Request body SName nametype not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.SName.NameString), "Request body SName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.SName.NameString, "Request body SName entries not as expected")
assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected")
assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected")
assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected")
assert.Equal(t, 0, len(a.ReqBody.Addresses), "Number of client addresses not empty")
assert.Equal(t, 0, len(a.ReqBody.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty")
assert.Equal(t, 0, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not empty")
}
//// Marshal Tests ////
func TestMarshalKDCReqBody(t *testing.T) {
t.Parallel()
var a KDCReqBody
v := "encode_krb5_kdc_req_body"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
// Marshal and re-unmarshal the result nd then compare
mb, err := a.Marshal()
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, b, mb, "Marshal bytes of KDCReqBody not as expected")
}
func TestMarshalASReq(t *testing.T) {
t.Parallel()
var a ASReq
v := "encode_krb5_as_req"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
mb, err := a.Marshal()
if err != nil {
t.Fatalf("Marshal of ticket errored: %v", err)
}
assert.Equal(t, b, mb, "Marshal bytes of ASReq not as expected")
}
func TestMarshalTGSReq(t *testing.T) {
t.Parallel()
var a TGSReq
v := "encode_krb5_tgs_req"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
mb, err := a.Marshal()
if err != nil {
t.Fatalf("Marshal of ticket errored: %v", err)
}
assert.Equal(t, b, mb, "Marshal bytes of TGSReq not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBCred.go 0000664 0000000 0000000 00000007374 13625372257 0024142 0 ustar 00root root 0000000 0000000 package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag"
"gopkg.in/jcmturner/gokrb5.v5/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
type marshalKRBCred struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
Tickets asn1.RawValue `asn1:"explicit,tag:2"`
EncPart types.EncryptedData `asn1:"explicit,tag:3"`
}
// KRBCred implements RFC 4120 KRB_CRED: https://tools.ietf.org/html/rfc4120#section-5.8.1.
type KRBCred struct {
PVNO int
MsgType int
Tickets []Ticket
EncPart types.EncryptedData
DecryptedEncPart EncKrbCredPart
}
// EncKrbCredPart is the encrypted part of KRB_CRED.
type EncKrbCredPart struct {
TicketInfo []KrbCredInfo `asn1:"explicit,tag:0"`
Nouce int `asn1:"optional,explicit,tag:1"`
Timestamp time.Time `asn1:"generalized,optional,explicit,tag:2"`
Usec int `asn1:"optional,explicit,tag:3"`
SAddress types.HostAddress `asn1:"optional,explicit,tag:4"`
RAddress types.HostAddress `asn1:"optional,explicit,tag:5"`
}
// KrbCredInfo is the KRB_CRED_INFO part of KRB_CRED.
type KrbCredInfo struct {
Key types.EncryptionKey `asn1:"explicit,tag:0"`
PRealm string `asn1:"generalstring,optional,explicit,tag:1"`
PName types.PrincipalName `asn1:"optional,explicit,tag:2"`
Flags asn1.BitString `asn1:"optional,explicit,tag:3"`
AuthTime time.Time `asn1:"generalized,optional,explicit,tag:4"`
StartTime time.Time `asn1:"generalized,optional,explicit,tag:5"`
EndTime time.Time `asn1:"generalized,optional,explicit,tag:6"`
RenewTill time.Time `asn1:"generalized,optional,explicit,tag:7"`
SRealm string `asn1:"optional,explicit,ia5,tag:8"`
SName types.PrincipalName `asn1:"optional,explicit,tag:9"`
CAddr types.HostAddresses `asn1:"optional,explicit,tag:10"`
}
// Unmarshal bytes b into the KRBCred struct.
func (k *KRBCred) Unmarshal(b []byte) error {
var m marshalKRBCred
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBCred))
if err != nil {
return processUnmarshalReplyError(b, err)
}
expectedMsgType := msgtype.KRB_CRED
if m.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_CRED. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
}
k.PVNO = m.PVNO
k.MsgType = m.MsgType
k.EncPart = m.EncPart
if len(m.Tickets.Bytes) > 0 {
k.Tickets, err = UnmarshalTicketsSequence(m.Tickets)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling tickets within KRB_CRED")
}
}
return nil
}
// DecryptEncPart decrypts the encrypted part of a KRB_CRED.
func (k *KRBCred) DecryptEncPart(key types.EncryptionKey) error {
b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.KRB_CRED_ENCPART)
if err != nil {
return krberror.Errorf(err, krberror.DecryptingError, "error decrypting KRB_CRED EncPart")
}
var denc EncKrbCredPart
err = denc.Unmarshal(b)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part of KRB_CRED")
}
k.DecryptedEncPart = denc
return nil
}
// Unmarshal bytes b into the encrypted part of KRB_CRED.
func (k *EncKrbCredPart) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncKrbCredPart))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling EncKrbCredPart")
}
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBCred_test.go 0000664 0000000 0000000 00000021535 13625372257 0025174 0 ustar 00root root 0000000 0000000 package messages
import (
"encoding/hex"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/addrtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestUnmarshalKRBCred(t *testing.T) {
t.Parallel()
var a KRBCred
v := "encode_krb5_cred"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_CRED, a.MsgType, "Message type not as expected")
assert.Equal(t, 2, len(a.Tickets), "Number of tickets not as expected")
for i, tkt := range a.Tickets {
assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Ticket (%v) ticket-vno not as expected", i+1))
assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Ticket (%v) realm not as expected", i+1))
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Ticket (%v) SName NameType not as expected", i+1))
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Ticket (%v) SName does not have the expected number of NameStrings", i+1))
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Ticket (%v) SName name string entries not as expected", i+1))
assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Ticket (%v) encPart etype not as expected", i+1))
assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Ticket (%v) encPart KVNO not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Ticket (%v) encPart cipher not as expected", i+1))
}
assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "encPart etype not as expected")
assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "encPart KVNO not as expected")
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.EncPart.Cipher, "encPart cipher not as expected")
}
func TestUnmarshalEncCredPart(t *testing.T) {
t.Parallel()
var a EncKrbCredPart
v := "encode_krb5_enc_cred_part"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, 2, len(a.TicketInfo), "Number of ticket info items not as expected")
for i, tkt := range a.TicketInfo {
assert.Equal(t, int32(1), tkt.Key.KeyType, fmt.Sprintf("Key type not as expected in ticket info item %d", i+1))
assert.Equal(t, []byte("12345678"), tkt.Key.KeyValue, fmt.Sprintf("Key value not as expected in ticket info item %d", i+1))
assert.Equal(t, testdata.TEST_REALM, tkt.PRealm, fmt.Sprintf("PRealm not as expected on ticket info item %d", i+1))
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.PName.NameType, fmt.Sprintf("Ticket info (%v) PName NameType not as expected", i+1))
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.PName.NameString), fmt.Sprintf("Ticket info (%v) PName does not have the expected number of NameStrings", i+1))
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.PName.NameString, fmt.Sprintf("Ticket info (%v) PName name string entries not as expected", i+1))
assert.Equal(t, "fedcba98", hex.EncodeToString(tkt.Flags.Bytes), fmt.Sprintf("Flags not as expected on ticket info %d", i+1))
assert.Equal(t, tt, tkt.AuthTime, fmt.Sprintf("Auth time value not as expected for ticket info %d", i+1))
assert.Equal(t, tt, tkt.StartTime, fmt.Sprintf("Start time value not as expected for ticket info %d", i+1))
assert.Equal(t, tt, tkt.EndTime, fmt.Sprintf("End time value not as expected for ticket info %d", i+1))
assert.Equal(t, tt, tkt.RenewTill, fmt.Sprintf("Renew Till time value not as expected for ticket info %d", i+1))
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Ticket info (%v) PName NameType not as expected", i+1))
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Ticket info (%v) PName does not have the expected number of NameStrings", i+1))
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Ticket info (%v) PName name string entries not as expected", i+1))
assert.Equal(t, 2, len(tkt.CAddr), "Number of client addresses not as expected")
for j, addr := range tkt.CAddr {
assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d within ticket info %d", j+1, i+1))
assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d within ticket info %d", j+1, i+1))
}
}
assert.Equal(t, testdata.TEST_NONCE, a.Nouce, "Nouce not as expected")
assert.Equal(t, tt, a.Timestamp, "Timestamp not as expected")
assert.Equal(t, 123456, a.Usec, "Microseconds not as expected")
assert.Equal(t, addrtype.IPv4, a.SAddress.AddrType, "SAddress type not as expected")
assert.Equal(t, "12d00023", hex.EncodeToString(a.SAddress.Address), "Address not as expected for SAddress")
assert.Equal(t, addrtype.IPv4, a.RAddress.AddrType, "RAddress type not as expected")
assert.Equal(t, "12d00023", hex.EncodeToString(a.RAddress.Address), "Address not as expected for RAddress")
}
func TestUnmarshalEncCredPart_optionalsNULL(t *testing.T) {
t.Parallel()
var a EncKrbCredPart
v := "encode_krb5_enc_cred_part(optionalsNULL)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, 2, len(a.TicketInfo), "Number of ticket info items not as expected")
//1st Ticket
i := 0
assert.Equal(t, int32(1), a.TicketInfo[i].Key.KeyType, fmt.Sprintf("Key type not as expected in ticket info item %d", i+1))
assert.Equal(t, []byte("12345678"), a.TicketInfo[i].Key.KeyValue, fmt.Sprintf("Key value not as expected in ticket info item %d", i+1))
//2nd Ticket
i = 1
assert.Equal(t, int32(1), a.TicketInfo[i].Key.KeyType, fmt.Sprintf("Key type not as expected in ticket info item %d", i+1))
assert.Equal(t, []byte("12345678"), a.TicketInfo[i].Key.KeyValue, fmt.Sprintf("Key value not as expected in ticket info item %d", i+1))
assert.Equal(t, testdata.TEST_REALM, a.TicketInfo[i].PRealm, fmt.Sprintf("PRealm not as expected on ticket info item %d", i+1))
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.TicketInfo[i].PName.NameType, fmt.Sprintf("Ticket info (%v) PName NameType not as expected", i+1))
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.TicketInfo[i].PName.NameString), fmt.Sprintf("Ticket info (%v) PName does not have the expected number of NameStrings", i+1))
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.TicketInfo[i].PName.NameString, fmt.Sprintf("Ticket info (%v) PName name string entries not as expected", i+1))
assert.Equal(t, "fedcba98", hex.EncodeToString(a.TicketInfo[i].Flags.Bytes), fmt.Sprintf("Flags not as expected on ticket info %d", i+1))
assert.Equal(t, tt, a.TicketInfo[i].AuthTime, fmt.Sprintf("Auth time value not as expected for ticket info %d", i+1))
assert.Equal(t, tt, a.TicketInfo[i].StartTime, fmt.Sprintf("Start time value not as expected for ticket info %d", i+1))
assert.Equal(t, tt, a.TicketInfo[i].EndTime, fmt.Sprintf("End time value not as expected for ticket info %d", i+1))
assert.Equal(t, tt, a.TicketInfo[i].RenewTill, fmt.Sprintf("Renew Till time value not as expected for ticket info %d", i+1))
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.TicketInfo[i].SName.NameType, fmt.Sprintf("Ticket info (%v) PName NameType not as expected", i+1))
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.TicketInfo[i].SName.NameString), fmt.Sprintf("Ticket info (%v) PName does not have the expected number of NameStrings", i+1))
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.TicketInfo[i].SName.NameString, fmt.Sprintf("Ticket info (%v) PName name string entries not as expected", i+1))
assert.Equal(t, 2, len(a.TicketInfo[i].CAddr), "Number of client addresses not as expected")
for j, addr := range a.TicketInfo[i].CAddr {
assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d within ticket info %d", j+1, i+1))
assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d within ticket info %d", j+1, i+1))
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBError.go 0000664 0000000 0000000 00000005644 13625372257 0024354 0 ustar 00root root 0000000 0000000 // Package messages implements Kerberos 5 message types and methods.
package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag"
"gopkg.in/jcmturner/gokrb5.v5/iana/errorcode"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// KRBError implements RFC 4120 KRB_ERROR: https://tools.ietf.org/html/rfc4120#section-5.9.1.
type KRBError struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
CTime time.Time `asn1:"generalized,optional,explicit,tag:2"`
Cusec int `asn1:"optional,explicit,tag:3"`
STime time.Time `asn1:"generalized,explicit,tag:4"`
Susec int `asn1:"explicit,tag:5"`
ErrorCode int32 `asn1:"explicit,tag:6"`
CRealm string `asn1:"generalstring,optional,explicit,tag:7"`
CName types.PrincipalName `asn1:"optional,explicit,tag:8"`
Realm string `asn1:"generalstring,explicit,tag:9"`
SName types.PrincipalName `asn1:"explicit,tag:10"`
EText string `asn1:"generalstring,optional,explicit,tag:11"`
EData []byte `asn1:"optional,explicit,tag:12"`
}
// NewKRBError creates a new KRBError.
func NewKRBError(sname types.PrincipalName, realm string, code int32, etext string) KRBError {
t := time.Now().UTC()
return KRBError{
PVNO: iana.PVNO,
MsgType: msgtype.KRB_ERROR,
STime: t,
Susec: int((t.UnixNano() / int64(time.Microsecond)) - (t.Unix() * 1e6)),
ErrorCode: code,
SName: sname,
Realm: realm,
EText: etext,
}
}
// Unmarshal bytes b into the KRBError struct.
func (k *KRBError) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBError))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "KRB_ERROR unmarshal error")
}
expectedMsgType := msgtype.KRB_ERROR
if k.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_ERROR. Expected: %v; Actual: %v", expectedMsgType, k.MsgType)
}
return nil
}
// Error method implementing error interface on KRBError struct.
func (k KRBError) Error() string {
etxt := fmt.Sprintf("KRB Error: %s", errorcode.Lookup(k.ErrorCode))
if k.EText != "" {
etxt = fmt.Sprintf("%s - %s", etxt, k.EText)
}
return etxt
}
func processUnmarshalReplyError(b []byte, err error) error {
switch err.(type) {
case asn1.StructuralError:
var krberr KRBError
tmperr := krberr.Unmarshal(b)
if tmperr != nil {
return krberror.Errorf(err, krberror.EncodingError, "failed to unmarshal KDC's reply")
}
return krberr
default:
return krberror.Errorf(err, krberror.EncodingError, "failed to unmarshal KDC's reply")
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBError_test.go 0000664 0000000 0000000 00000007056 13625372257 0025412 0 ustar 00root root 0000000 0000000 package messages
import (
"encoding/hex"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/errorcode"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestUnmarshalKRBError(t *testing.T) {
t.Parallel()
var a KRBError
v := "encode_krb5_error"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO is not as expected")
assert.Equal(t, msgtype.KRB_ERROR, a.MsgType, "Message type is not as expected")
assert.Equal(t, tt, a.CTime, "CTime not as expected")
assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected")
assert.Equal(t, tt, a.STime, "STime not as expected")
assert.Equal(t, 123456, a.Susec, "Service microseconds not as expected")
assert.Equal(t, errorcode.KRB_ERR_GENERIC, a.ErrorCode, "Error code not as expected")
assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected")
assert.Equal(t, testdata.TEST_REALM, a.Realm, "Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "Ticket SName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.SName.NameString), "Ticket SName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "Ticket SName name string entries not as expected")
assert.Equal(t, "krb5data", a.EText, "EText not as expected")
assert.Equal(t, []byte("krb5data"), a.EData, "EData not as expected")
}
func TestUnmarshalKRBError_optionalsNULL(t *testing.T) {
t.Parallel()
var a KRBError
v := "encode_krb5_error(optionalsNULL)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO is not as expected")
assert.Equal(t, msgtype.KRB_ERROR, a.MsgType, "Message type is not as expected")
assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected")
assert.Equal(t, tt, a.STime, "STime not as expected")
assert.Equal(t, 123456, a.Susec, "Service microseconds not as expected")
assert.Equal(t, errorcode.KRB_ERR_GENERIC, a.ErrorCode, "Error code not as expected")
assert.Equal(t, testdata.TEST_REALM, a.Realm, "Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "Ticket SName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.SName.NameString), "Ticket SName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "Ticket SName name string entries not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBPriv.go 0000664 0000000 0000000 00000006724 13625372257 0024203 0 ustar 00root root 0000000 0000000 package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/asn1tools"
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag"
"gopkg.in/jcmturner/gokrb5.v5/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// KRBPriv implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.7.1.
type KRBPriv struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
EncPart types.EncryptedData `asn1:"explicit,tag:3"`
DecryptedEncPart EncKrbPrivPart `asn1:"optional,omitempty"` // Not part of ASN1 bytes so marked as optional so unmarshalling works
}
// EncKrbPrivPart is the encrypted part of KRB_PRIV.
type EncKrbPrivPart struct {
UserData []byte `asn1:"explicit,tag:0"`
Timestamp time.Time `asn1:"generalized,optional,explicit,tag:1"`
Usec int `asn1:"optional,explicit,tag:2"`
SequenceNumber int64 `asn1:"optional,explicit,tag:3"`
SAddress types.HostAddress `asn1:"explicit,tag:4"`
RAddress types.HostAddress `asn1:"optional,explicit,tag:5"`
}
// NewKRBPriv returns a new KRBPriv type.
func NewKRBPriv(part EncKrbPrivPart) KRBPriv {
return KRBPriv{
PVNO: iana.PVNO,
MsgType: msgtype.KRB_PRIV,
DecryptedEncPart: part,
}
}
// Unmarshal bytes b into the KRBPriv struct.
func (k *KRBPriv) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBPriv))
if err != nil {
return processUnmarshalReplyError(b, err)
}
expectedMsgType := msgtype.KRB_PRIV
if k.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_PRIV. Expected: %v; Actual: %v", expectedMsgType, k.MsgType)
}
return nil
}
// Unmarshal bytes b into the EncKrbPrivPart struct.
func (k *EncKrbPrivPart) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncKrbPrivPart))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "KRB_PRIV unmarshal error")
}
return nil
}
// Marshal the KRBPriv.
func (k *KRBPriv) Marshal() ([]byte, error) {
tk := KRBPriv{
PVNO: k.PVNO,
MsgType: k.MsgType,
EncPart: k.EncPart,
}
b, err := asn1.Marshal(tk)
if err != nil {
return []byte{}, err
}
b = asn1tools.AddASNAppTag(b, asnAppTag.KRBPriv)
return b, nil
}
// EncryptEncPart encrypts the DecryptedEncPart within the KRBPriv.
// Use to prepare for marshaling.
func (k *KRBPriv) EncryptEncPart(key types.EncryptionKey) error {
b, err := asn1.Marshal(k.DecryptedEncPart)
if err != nil {
return err
}
b = asn1tools.AddASNAppTag(b, asnAppTag.EncKrbPrivPart)
k.EncPart, err = crypto.GetEncryptedData(b, key, keyusage.KRB_PRIV_ENCPART, 1)
if err != nil {
return err
}
return nil
}
// DecryptEncPart decrypts the encrypted part of the KRBPriv message.
func (k *KRBPriv) DecryptEncPart(key types.EncryptionKey) error {
b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.KRB_PRIV_ENCPART)
if err != nil {
return fmt.Errorf("error decrypting KRBPriv EncPart: %v", err)
}
err = k.DecryptedEncPart.Unmarshal(b)
if err != nil {
return fmt.Errorf("error unmarshaling encrypted part: %v", err)
}
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBPriv_test.go 0000664 0000000 0000000 00000010674 13625372257 0025241 0 ustar 00root root 0000000 0000000 package messages
import (
"encoding/hex"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/addrtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
func TestUnmarshalKRBPriv(t *testing.T) {
t.Parallel()
var a KRBPriv
v := "encode_krb5_priv"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_PRIV, a.MsgType, "Message type not as expected")
assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "EncPart KVNO not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "EncPart etype not as expected")
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.EncPart.Cipher, "Cipher text of EncPart not as expected")
}
func TestUnmarshalEncPrivPart(t *testing.T) {
t.Parallel()
var a EncKrbPrivPart
v := "encode_krb5_enc_priv_part"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, "krb5data", string(a.UserData), "User data not as expected")
assert.Equal(t, tt, a.Timestamp, "Timestamp not as expected")
assert.Equal(t, 123456, a.Usec, "Microseconds not as expected")
assert.Equal(t, int64(17), a.SequenceNumber, "Sequence number not as expected")
assert.Equal(t, addrtype.IPv4, a.SAddress.AddrType, "SAddress type not as expected")
assert.Equal(t, "12d00023", hex.EncodeToString(a.SAddress.Address), "Address not as expected for SAddress")
assert.Equal(t, addrtype.IPv4, a.RAddress.AddrType, "RAddress type not as expected")
assert.Equal(t, "12d00023", hex.EncodeToString(a.RAddress.Address), "Address not as expected for RAddress")
}
func TestUnmarshalEncPrivPart_optionalsNULL(t *testing.T) {
t.Parallel()
var a EncKrbPrivPart
v := "encode_krb5_enc_priv_part(optionalsNULL)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, "krb5data", string(a.UserData), "User data not as expected")
assert.Equal(t, addrtype.IPv4, a.SAddress.AddrType, "SAddress type not as expected")
assert.Equal(t, "12d00023", hex.EncodeToString(a.SAddress.Address), "Address not as expected for SAddress")
}
func TestMarshalKRBPriv(t *testing.T) {
t.Parallel()
var a KRBPriv
v := "encode_krb5_priv"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v", v, err)
}
mb, err := a.Marshal()
if err != nil {
t.Fatalf("error marshaling KRBPriv: %v", err)
}
assert.Equal(t, b, mb, "marshaled bytes not as expected")
v = "encode_krb5_enc_priv_part"
be, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v", v, err)
}
err = a.DecryptedEncPart.Unmarshal(be)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v", v, err)
}
mb, err = a.Marshal()
if err != nil {
t.Fatalf("error marshaling KRBPriv: %v", err)
}
assert.Equal(t, b, mb, "marshaled bytes not as expected when it has decrypted encpart")
}
func TestKRBPriv_EncryptEncPart(t *testing.T) {
t.Parallel()
var a KRBPriv
v := "encode_krb5_priv"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v", v, err)
}
v = "encode_krb5_enc_priv_part"
b, err = hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v", v, err)
}
err = a.DecryptedEncPart.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v", v, err)
}
key := types.EncryptionKey{
KeyType: int32(18),
KeyValue: []byte("12345678901234567890123456789012"),
}
err = a.EncryptEncPart(key)
if err != nil {
t.Fatalf("error encrypting encpart: %v", err)
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBSafe.go 0000664 0000000 0000000 00000003022 13625372257 0024125 0 ustar 00root root 0000000 0000000 package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// KRBSafe implements RFC 4120 KRB_SAFE: https://tools.ietf.org/html/rfc4120#section-5.6.1.
type KRBSafe struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
SafeBody KRBSafeBody `asn1:"explicit,tag:2"`
Cksum types.Checksum `asn1:"explicit,tag:3"`
}
// KRBSafeBody implements the KRB_SAFE_BODY of KRB_SAFE.
type KRBSafeBody struct {
UserData []byte `asn1:"explicit,tag:0"`
Timestamp time.Time `asn1:"generalized,optional,explicit,tag:1"`
Usec int `asn1:"optional,explicit,tag:2"`
SequenceNumber int64 `asn1:"optional,explicit,tag:3"`
SAddress types.HostAddress `asn1:"explicit,tag:4"`
RAddress types.HostAddress `asn1:"optional,explicit,tag:5"`
}
// Unmarshal bytes b into the KRBSafe struct.
func (s *KRBSafe) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, s, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBSafe))
if err != nil {
return processUnmarshalReplyError(b, err)
}
expectedMsgType := msgtype.KRB_SAFE
if s.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_SAFE. Expected: %v; Actual: %v", expectedMsgType, s.MsgType)
}
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBSafe_test.go 0000664 0000000 0000000 00000005231 13625372257 0025170 0 ustar 00root root 0000000 0000000 package messages
import (
"encoding/hex"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/addrtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/msgtype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestUnmarshalKRBSafe(t *testing.T) {
t.Parallel()
var a KRBSafe
v := "encode_krb5_safe"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_SAFE, a.MsgType, "MsgType is not as expected")
assert.Equal(t, []byte("krb5data"), a.SafeBody.UserData, "Safe body userdata not as expected")
assert.Equal(t, tt, a.SafeBody.Timestamp, "Safe body timestamp not as expected")
assert.Equal(t, 123456, a.SafeBody.Usec, "Safe body microseconds not as expected")
assert.Equal(t, int64(17), a.SafeBody.SequenceNumber, "Safe body sequence number not as expected")
assert.Equal(t, addrtype.IPv4, a.SafeBody.SAddress.AddrType, "SAddress type not as expected")
assert.Equal(t, "12d00023", hex.EncodeToString(a.SafeBody.SAddress.Address), "SAddress not as expected")
assert.Equal(t, addrtype.IPv4, a.SafeBody.RAddress.AddrType, "RAddress type not as expected")
assert.Equal(t, "12d00023", hex.EncodeToString(a.SafeBody.RAddress.Address), "RAddress not as expected")
assert.Equal(t, int32(1), a.Cksum.CksumType, "Checksum type not as expected")
assert.Equal(t, []byte("1234"), a.Cksum.Checksum, "Checksum not as expected")
}
func TestUnmarshalKRBSafe_optionalsNULL(t *testing.T) {
t.Parallel()
var a KRBSafe
v := "encode_krb5_safe(optionalsNULL)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected")
assert.Equal(t, msgtype.KRB_SAFE, a.MsgType, "MsgType is not as expected")
assert.Equal(t, []byte("krb5data"), a.SafeBody.UserData, "Safe body userdata not as expected")
assert.Equal(t, addrtype.IPv4, a.SafeBody.SAddress.AddrType, "SAddress type not as expected")
assert.Equal(t, "12d00023", hex.EncodeToString(a.SafeBody.SAddress.Address), "SAddress not as expected")
assert.Equal(t, int32(1), a.Cksum.CksumType, "Checksum type not as expected")
assert.Equal(t, []byte("1234"), a.Cksum.Checksum, "Checksum not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/Ticket.go 0000664 0000000 0000000 00000021216 13625372257 0024140 0 ustar 00root root 0000000 0000000 package messages
import (
"crypto/rand"
"fmt"
"strings"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/asn1tools"
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/adtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag"
"gopkg.in/jcmturner/gokrb5.v5/iana/errorcode"
"gopkg.in/jcmturner/gokrb5.v5/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/pac"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.3
// Ticket implements the Kerberos ticket.
type Ticket struct {
TktVNO int `asn1:"explicit,tag:0"`
Realm string `asn1:"generalstring,explicit,tag:1"`
SName types.PrincipalName `asn1:"explicit,tag:2"`
EncPart types.EncryptedData `asn1:"explicit,tag:3"`
DecryptedEncPart EncTicketPart `asn1:"optional"` // Not part of ASN1 bytes so marked as optional so unmarshalling works
}
// EncTicketPart is the encrypted part of the Ticket.
type EncTicketPart struct {
Flags asn1.BitString `asn1:"explicit,tag:0"`
Key types.EncryptionKey `asn1:"explicit,tag:1"`
CRealm string `asn1:"generalstring,explicit,tag:2"`
CName types.PrincipalName `asn1:"explicit,tag:3"`
Transited TransitedEncoding `asn1:"explicit,tag:4"`
AuthTime time.Time `asn1:"generalized,explicit,tag:5"`
StartTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
EndTime time.Time `asn1:"generalized,explicit,tag:7"`
RenewTill time.Time `asn1:"generalized,explicit,optional,tag:8"`
CAddr types.HostAddresses `asn1:"explicit,optional,tag:9"`
AuthorizationData types.AuthorizationData `asn1:"explicit,optional,tag:10"`
}
// TransitedEncoding part of the ticket's encrypted part.
type TransitedEncoding struct {
TRType int32 `asn1:"explicit,tag:0"`
Contents []byte `asn1:"explicit,tag:1"`
}
// NewTicket creates a new Ticket instance.
func NewTicket(cname types.PrincipalName, crealm string, sname types.PrincipalName, srealm string, flags asn1.BitString, sktab keytab.Keytab, eTypeID int32, kvno int, authTime, startTime, endTime, renewTill time.Time) (Ticket, types.EncryptionKey, error) {
etype, err := crypto.GetEtype(eTypeID)
if err != nil {
return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error getting etype for new ticket")
}
ks := etype.GetKeyByteSize()
kv := make([]byte, ks, ks)
rand.Read(kv)
sessionKey := types.EncryptionKey{
KeyType: eTypeID,
KeyValue: kv,
}
etp := EncTicketPart{
Flags: flags,
Key: sessionKey,
CRealm: crealm,
CName: cname,
Transited: TransitedEncoding{},
AuthTime: authTime,
StartTime: startTime,
EndTime: endTime,
RenewTill: renewTill,
}
b, err := asn1.Marshal(etp)
if err != nil {
return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncodingError, "error marshalling ticket encpart")
}
b = asn1tools.AddASNAppTag(b, asnAppTag.EncTicketPart)
skey, err := sktab.GetEncryptionKey(sname.NameString, srealm, kvno, eTypeID)
if err != nil {
return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error getting encryption key for new ticket")
}
ed, err := crypto.GetEncryptedData(b, skey, keyusage.KDC_REP_TICKET, kvno)
if err != nil {
return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error encrypting ticket encpart")
}
tkt := Ticket{
TktVNO: iana.PVNO,
Realm: srealm,
SName: sname,
EncPart: ed,
}
return tkt, sessionKey, nil
}
// Unmarshal bytes b into a Ticket struct.
func (t *Ticket) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.Ticket))
return err
}
// Marshal the Ticket.
func (t *Ticket) Marshal() ([]byte, error) {
b, err := asn1.Marshal(*t)
if err != nil {
return nil, err
}
b = asn1tools.AddASNAppTag(b, asnAppTag.Ticket)
return b, nil
}
// Unmarshal bytes b into the EncTicketPart struct.
func (t *EncTicketPart) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.EncTicketPart))
return err
}
// UnmarshalTicket returns a ticket from the bytes provided.
func UnmarshalTicket(b []byte) (t Ticket, err error) {
_, err = asn1.UnmarshalWithParams(b, &t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.Ticket))
return
}
// UnmarshalTicketsSequence returns a slice of Tickets from a raw ASN1 value.
func UnmarshalTicketsSequence(in asn1.RawValue) ([]Ticket, error) {
//This is a workaround to a asn1 decoding issue in golang - https://github.com/golang/go/issues/17321. It's not pretty I'm afraid
//We pull out raw values from the larger raw value (that is actually the data of the sequence of raw values) and track our position moving along the data.
b := in.Bytes
// Ignore the head of the asn1 stream (1 byte for tag and those for the length) as this is what tells us its a sequence but we're handling it ourselves
p := 1 + asn1tools.GetNumberBytesInLengthHeader(in.Bytes)
var tkts []Ticket
var raw asn1.RawValue
for p < (len(b)) {
_, err := asn1.UnmarshalWithParams(b[p:], &raw, fmt.Sprintf("application,tag:%d", asnAppTag.Ticket))
if err != nil {
return nil, fmt.Errorf("unmarshaling sequence of tickets failed geting length of ticket: %v", err)
}
t, err := UnmarshalTicket(b[p:])
if err != nil {
return nil, fmt.Errorf("unmarshaling sequence of tickets failed: %v", err)
}
p += len(raw.FullBytes)
tkts = append(tkts, t)
}
MarshalTicketSequence(tkts)
return tkts, nil
}
// MarshalTicketSequence marshals a slice of Tickets returning an ASN1 raw value containing the ticket sequence.
func MarshalTicketSequence(tkts []Ticket) (asn1.RawValue, error) {
raw := asn1.RawValue{
Class: 2,
IsCompound: true,
}
if len(tkts) < 1 {
// There are no tickets to marshal
return raw, nil
}
var btkts []byte
for i, t := range tkts {
b, err := t.Marshal()
if err != nil {
return raw, fmt.Errorf("error marshaling ticket number %d in seqence of tickets", i+1)
}
btkts = append(btkts, b...)
}
btkts = append(asn1tools.MarshalLengthBytes(len(btkts)), btkts...)
btkts = append([]byte{byte(32 + asn1.TagSequence)}, btkts...)
raw.Bytes = btkts
// If we need to create the full bytes then identifier octet is "context-specific" = 128 + "constructed" + 32 + the wrapping explicit tag (11)
//fmt.Fprintf(os.Stderr, "mRaw fb: %v\n", raw.FullBytes)
return raw, nil
}
// DecryptEncPart decrypts the encrypted part of the ticket.
func (t *Ticket) DecryptEncPart(keytab keytab.Keytab, ktprinc string) error {
var upn types.PrincipalName
realm := t.Realm
if ktprinc != "" {
var r string
upn, r = types.ParseSPNString(ktprinc)
if r != "" {
realm = r
}
} else {
upn = t.SName
}
key, err := keytab.GetEncryptionKey(upn.NameString, realm, t.EncPart.KVNO, t.EncPart.EType)
if err != nil {
return NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", err))
}
b, err := crypto.DecryptEncPart(t.EncPart, key, keyusage.KDC_REP_TICKET)
if err != nil {
return fmt.Errorf("error decrypting Ticket EncPart: %v", err)
}
var denc EncTicketPart
err = denc.Unmarshal(b)
if err != nil {
return fmt.Errorf("error unmarshaling encrypted part: %v", err)
}
t.DecryptedEncPart = denc
return nil
}
// GetPACType returns a Microsoft PAC that has been extracted from the ticket and processed.
func (t *Ticket) GetPACType(keytab keytab.Keytab, ktprinc string) (bool, pac.PACType, error) {
var isPAC bool
for _, ad := range t.DecryptedEncPart.AuthorizationData {
if ad.ADType == adtype.ADIfRelevant {
var ad2 types.AuthorizationData
err := ad2.Unmarshal(ad.ADData)
if err != nil {
continue
}
if ad2[0].ADType == adtype.ADWin2KPAC {
isPAC = true
var p pac.PACType
err = p.Unmarshal(ad2[0].ADData)
if err != nil {
return isPAC, p, fmt.Errorf("error unmarshaling PAC: %v", err)
}
var upn []string
if ktprinc != "" {
upn = strings.Split(ktprinc, "/")
} else {
upn = t.SName.NameString
}
key, err := keytab.GetEncryptionKey(upn, t.Realm, t.EncPart.KVNO, t.EncPart.EType)
if err != nil {
return isPAC, p, NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", err))
}
err = p.ProcessPACInfoBuffers(key)
return isPAC, p, err
}
}
}
return isPAC, pac.PACType{}, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/Ticket_test.go 0000664 0000000 0000000 00000015416 13625372257 0025204 0 ustar 00root root 0000000 0000000 package messages
import (
"encoding/hex"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/addrtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/adtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/iana/trtype"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
func TestUnmarshalTicket(t *testing.T) {
t.Parallel()
var a Ticket
v := "encode_krb5_ticket"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, iana.PVNO, a.TktVNO, "Ticket version number not as expected")
assert.Equal(t, testdata.TEST_REALM, a.Realm, "Realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.SName.NameString), "SName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "SName name strings not as expected")
assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Etype of Ticket EncPart not as expected")
assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "KNVO of Ticket EncPart not as expected")
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.EncPart.Cipher, "Cipher of Ticket EncPart not as expected")
}
func TestUnmarshalEncTicketPart(t *testing.T) {
t.Parallel()
var a EncTicketPart
v := "encode_krb5_enc_tkt_part"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, "fedcba98", hex.EncodeToString(a.Flags.Bytes), "Flags not as expected")
assert.Equal(t, int32(1), a.Key.KeyType, "Key type not as expected")
assert.Equal(t, []byte("12345678"), a.Key.KeyValue, "Key value not as expected")
assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName type not as expected")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName string entries not as expected")
assert.Equal(t, trtype.DOMAIN_X500_COMPRESS, a.Transited.TRType, "Transisted type not as expected")
assert.Equal(t, []byte("EDU,MIT.,ATHENA.,WASHINGTON.EDU,CS."), a.Transited.Contents, "Transisted content not as expected")
assert.Equal(t, tt, a.AuthTime, "Auth time not as expected")
assert.Equal(t, tt, a.StartTime, "Start time not as expected")
assert.Equal(t, tt, a.EndTime, "End time not as expected")
assert.Equal(t, tt, a.RenewTill, "Renew Till time not as expected")
assert.Equal(t, 2, len(a.CAddr), "Number of client addresses not as expected")
for i, addr := range a.CAddr {
assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d", i+1))
assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d", i+1))
}
for i, ele := range a.AuthorizationData {
assert.Equal(t, adtype.ADIfRelevant, ele.ADType, fmt.Sprintf("Authorization data type of element %d not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_AUTHORIZATION_DATA_VALUE), ele.ADData, fmt.Sprintf("Authorization data of element %d not as expected", i+1))
}
}
func TestUnmarshalEncTicketPart_optionalsNULL(t *testing.T) {
t.Parallel()
var a EncTicketPart
v := "encode_krb5_enc_tkt_part(optionalsNULL)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, "fedcba98", hex.EncodeToString(a.Flags.Bytes), "Flags not as expected")
assert.Equal(t, int32(1), a.Key.KeyType, "Key type not as expected")
assert.Equal(t, []byte("12345678"), a.Key.KeyValue, "Key value not as expected")
assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName type not as expected")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName string entries not as expected")
assert.Equal(t, trtype.DOMAIN_X500_COMPRESS, a.Transited.TRType, "Transisted type not as expected")
assert.Equal(t, []byte("EDU,MIT.,ATHENA.,WASHINGTON.EDU,CS."), a.Transited.Contents, "Transisted content not as expected")
assert.Equal(t, tt, a.AuthTime, "Auth time not as expected")
assert.Equal(t, tt, a.EndTime, "End time not as expected")
}
func TestMarshalTicket(t *testing.T) {
t.Parallel()
var a Ticket
v := "encode_krb5_ticket"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
mb, err := a.Marshal()
if err != nil {
t.Fatalf("Marshal of ticket errored: %v", err)
}
assert.Equal(t, b, mb, "Marshalled bytes not as expected")
}
func TestAuthorizationData_GetPACType_GOKRB5TestData(t *testing.T) {
t.Parallel()
v := "PAC_AuthorizationData_GOKRB5"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
var a types.AuthorizationData
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
tkt := Ticket{
Realm: "TEST.GOKRB5",
EncPart: types.EncryptedData{
EType: 18,
KVNO: 2,
},
DecryptedEncPart: EncTicketPart{
AuthorizationData: a,
},
}
b, _ = hex.DecodeString(testdata.SYSHTTP_KEYTAB)
kt, _ := keytab.Parse(b)
isPAC, pac, err := tkt.GetPACType(kt, "sysHTTP")
if err != nil {
t.Fatalf("Error getting PAC Type: %v\n", err)
}
assert.True(t, isPAC, "PAC should be present")
assert.Equal(t, 5, len(pac.Buffers), "Number of buffers not as expected")
assert.Equal(t, uint32(5), pac.CBuffers, "Count of buffers not as expected")
assert.Equal(t, uint32(0), pac.Version, "PAC version not as expected")
assert.NotNil(t, pac.KerbValidationInfo, "PAC Kerb Validation info is nil")
assert.NotNil(t, pac.ClientInfo, "PAC Client Info info is nil")
assert.NotNil(t, pac.UPNDNSInfo, "PAC UPN DNS Info info is nil")
assert.NotNil(t, pac.KDCChecksum, "PAC KDC Checksum info is nil")
assert.NotNil(t, pac.ServerChecksum, "PAC Server checksum info is nil")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/ 0000775 0000000 0000000 00000000000 13625372257 0022261 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/claims.go 0000664 0000000 0000000 00000021776 13625372257 0024075 0 ustar 00root root 0000000 0000000 package mstypes
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// Compression format assigned numbers.
const (
CompressionFormatNone uint16 = 0
CompressionFormatLZNT1 uint16 = 2
CompressionFormatXPress uint16 = 3
CompressionFormatXPressHuff uint16 = 4
)
// ClaimsSourceType
const ClaimsSourceTypeAD uint16 = 1
// Claim Type assigned numbers
const (
ClaimTypeIDInt64 uint16 = 1
ClaimTypeIDUInt64 uint16 = 2
ClaimTypeIDString uint16 = 3
ClaimsTypeIDBoolean uint16 = 6
)
// ClaimsBlob implements https://msdn.microsoft.com/en-us/library/hh554119.aspx
type ClaimsBlob struct {
Size uint32
EncodedBlob []byte
}
// ReadClaimsBlob reads a ClaimsBlob from the byte slice.
func ReadClaimsBlob(b *[]byte, p *int, e *binary.ByteOrder) (c ClaimsBlob) {
c.Size = ndr.ReadUint32(b, p, e)
c.EncodedBlob = ndr.ReadBytes(b, p, int(c.Size), e)
return
}
// ClaimsSetMetadata implements https://msdn.microsoft.com/en-us/library/hh554073.aspx
type ClaimsSetMetadata struct {
claimsSetSize uint32
ClaimsSet ClaimsSet
CompressionFormat uint16 // Enum see constants for options
uncompressedClaimsSetSize uint32
ReservedType uint16
reservedFieldSize uint32
ReservedField []byte
}
// ClaimSet implements https://msdn.microsoft.com/en-us/library/hh554122.aspx
type ClaimsSet struct {
ClaimsArrayCount uint32
ClaimsArrays []ClaimsArray
ReservedType uint16
reservedFieldSize uint32
ReservedField []byte
}
// ClaimsArray implements https://msdn.microsoft.com/en-us/library/hh536458.aspx
type ClaimsArray struct {
ClaimsSourceType uint16
ClaimsCount uint32
ClaimsEntries []ClaimEntry
}
// ClaimEntry implements https://msdn.microsoft.com/en-us/library/hh536374.aspx
type ClaimEntry struct {
ID string //utf16string
Type uint16 // enums are 16 bit https://msdn.microsoft.com/en-us/library/windows/desktop/aa366818(v=vs.85).aspx
TypeInt64 ClaimTypeInt64
TypeUInt64 ClaimTypeUInt64
TypeString ClaimTypeString
TypeBool ClaimTypeBoolean
}
// ClaimTypeInt64 is a claim of type int64
type ClaimTypeInt64 struct {
ValueCount uint32
Value []int64
}
// ClaimTypeUInt64 is a claim of type uint64
type ClaimTypeUInt64 struct {
ValueCount uint32
Value []uint64
}
// ClaimTypeString is a claim of type string
type ClaimTypeString struct {
ValueCount uint32
Value []string
}
// ClaimTypeBoolean is a claim of type bool
type ClaimTypeBoolean struct {
ValueCount uint32
Value []bool
}
// ReadClaimsSetMetadata reads a ClaimsSetMetadata from the bytes slice.
func ReadClaimsSetMetadata(b *[]byte, p *int, e *binary.ByteOrder) (c ClaimsSetMetadata, err error) {
c.claimsSetSize = ndr.ReadUint32(b, p, e)
*p += 4 //Move over pointer to ClaimSet array
c.CompressionFormat = ndr.ReadUint16(b, p, e)
// TODO Currently compression is not supported so if it is compressed we just have to return.
if c.CompressionFormat != CompressionFormatNone {
*p = len(*b)
return
}
c.uncompressedClaimsSetSize = ndr.ReadUint32(b, p, e)
c.ReservedType = ndr.ReadUint16(b, p, e)
c.reservedFieldSize = ndr.ReadUint32(b, p, e)
*p += 4 //Move over pointer to ReservedField array
var ah ndr.ConformantArrayHeader
if c.claimsSetSize > 0 {
// ClaimsSet is a conformant array https://msdn.microsoft.com/en-us/library/windows/desktop/aa373603(v=vs.85).aspx
ah, err = ndr.ReadUniDimensionalConformantArrayHeader(b, p, e)
if err != nil {
return
}
if ah.MaxCount != int(c.claimsSetSize) {
err = errors.New("error with size of CLAIMS_SET array")
return
}
csb := ndr.ReadBytes(b, p, int(c.claimsSetSize), e)
//TODO put decompression here
c.ClaimsSet, err = ReadClaimsSet(csb)
if err != nil {
return
}
}
if c.reservedFieldSize > 0 {
ah, err = ndr.ReadUniDimensionalConformantArrayHeader(b, p, e)
if err != nil {
return
}
if ah.MaxCount != int(c.reservedFieldSize) {
err = errors.New("error with size of CLAIMS_SET_METADATA's reserved field array")
return
}
c.ReservedField = ndr.ReadBytes(b, p, int(c.reservedFieldSize), e)
}
return
}
// ReadClaimsSet reads a ClaimsSet from the bytes slice.
func ReadClaimsSet(b []byte) (c ClaimsSet, err error) {
ch, _, p, err := ndr.ReadHeaders(&b)
if err != nil {
err = fmt.Errorf("error parsing NDR byte stream headers of CLAIMS_SET: %v", err)
return
}
e := &ch.Endianness
//The next 4 bytes are an RPC unique pointer referent. We just skip these
p += 4
c.ClaimsArrayCount = ndr.ReadUint32(&b, &p, e)
p += 4 //Move over pointer to claims array
c.ReservedType = ndr.ReadUint16(&b, &p, e)
c.reservedFieldSize = ndr.ReadUint32(&b, &p, e)
p += 4 //Move over pointer to ReservedField array
var ah ndr.ConformantArrayHeader
if c.ClaimsArrayCount > 0 {
ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e)
if err != nil {
return
}
if ah.MaxCount != int(c.ClaimsArrayCount) {
err = errors.New("error with size of CLAIMS_SET's claims array")
return
}
c.ClaimsArrays = make([]ClaimsArray, c.ClaimsArrayCount, c.ClaimsArrayCount)
for i := range c.ClaimsArrays {
c.ClaimsArrays[i], err = ReadClaimsArray(&b, &p, e)
if err != nil {
return
}
}
}
if c.reservedFieldSize > 0 {
ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e)
if err != nil {
return
}
if ah.MaxCount != int(c.reservedFieldSize) {
err = errors.New("error with size of CLAIMS_SET's reserved field array")
return
}
c.ReservedField = ndr.ReadBytes(&b, &p, int(c.reservedFieldSize), e)
}
return c, nil
}
// ReadClaimsArray reads a ClaimsArray from the bytes slice.
func ReadClaimsArray(b *[]byte, p *int, e *binary.ByteOrder) (c ClaimsArray, err error) {
c.ClaimsSourceType = ndr.ReadUint16(b, p, e)
c.ClaimsCount = ndr.ReadUint32(b, p, e)
*p += 4 //Move over pointer to claims array
ah, err := ndr.ReadUniDimensionalConformantArrayHeader(b, p, e)
if err != nil {
return
}
if ah.MaxCount != int(c.ClaimsCount) {
err = errors.New("error with size of CLAIMS_ARRAY's claims entries")
return
}
c.ClaimsEntries = make([]ClaimEntry, c.ClaimsCount, c.ClaimsCount)
for i := range c.ClaimsEntries {
var vc uint32
c.ClaimsEntries[i].Type, vc, err = ReadClaimEntriesUnionHeaders(b, p, e)
if err != nil {
return
}
switch c.ClaimsEntries[i].Type {
case ClaimTypeIDInt64:
c.ClaimsEntries[i].TypeInt64.ValueCount = vc
case ClaimTypeIDUInt64:
c.ClaimsEntries[i].TypeUInt64.ValueCount = vc
case ClaimTypeIDString:
c.ClaimsEntries[i].TypeString.ValueCount = vc
case ClaimsTypeIDBoolean:
c.ClaimsEntries[i].TypeBool.ValueCount = vc
}
}
for i := range c.ClaimsEntries {
err = FillClaimEntry(b, p, e, &c.ClaimsEntries[i])
if err != nil {
return
}
}
return
}
func ReadClaimEntriesUnionHeaders(b *[]byte, p *int, e *binary.ByteOrder) (uint16, uint32, error) {
*p += 4
// This is an NDR union: http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagfcjh_39
t1 := ndr.ReadUint16(b, p, e)
t2 := ndr.ReadUint16(b, p, e)
if t1 != t2 {
return 0, 0, ndr.Malformed{EText: "malformed NDR encoding of CLAIM_ENTRY union"}
}
vc := ndr.ReadUint32(b, p, e)
*p += 4 //Move over pointer to array of values
return t1, vc, nil
}
// FillClaimEntry reads a ClaimEntry from the bytes slice.
func FillClaimEntry(b *[]byte, p *int, e *binary.ByteOrder, c *ClaimEntry) (err error) {
c.ID, err = ndr.ReadConformantVaryingString(b, p, e)
if err != nil {
return
}
ah, err := ndr.ReadUniDimensionalConformantArrayHeader(b, p, e)
if err != nil {
return
}
switch c.Type {
case ClaimTypeIDInt64:
if ah.MaxCount != int(c.TypeInt64.ValueCount) {
return errors.New("error with size of CLAIM_ENTRY's value")
}
c.TypeInt64.Value = make([]int64, c.TypeInt64.ValueCount, c.TypeInt64.ValueCount)
for i := range c.TypeInt64.Value {
buf := bytes.NewReader((*b)[*p : *p+8])
err = binary.Read(buf, *e, &c.TypeInt64.Value[i])
if err != nil {
return
}
*p += 8 // progress position for a uint64
}
case ClaimTypeIDUInt64:
if ah.MaxCount != int(c.TypeUInt64.ValueCount) {
return errors.New("error with size of CLAIM_ENTRY's value")
}
c.TypeUInt64.Value = make([]uint64, c.TypeUInt64.ValueCount, c.TypeUInt64.ValueCount)
for i := range c.TypeUInt64.Value {
c.TypeUInt64.Value[i] = ndr.ReadUint64(b, p, e)
}
case ClaimTypeIDString:
if ah.MaxCount != int(c.TypeString.ValueCount) {
return errors.New("error with size of CLAIM_ENTRY's value")
}
c.TypeString.Value = make([]string, c.TypeString.ValueCount, c.TypeString.ValueCount)
*p += 4 * (int(c.TypeString.ValueCount)) // Move over pointers
for i := range c.TypeString.Value {
c.TypeString.Value[i], err = ndr.ReadConformantVaryingString(b, p, e)
if err != nil {
return
}
}
case ClaimsTypeIDBoolean:
if ah.MaxCount != int(c.TypeBool.ValueCount) {
return errors.New("error with size of CLAIM_ENTRY's value")
}
c.TypeBool.Value = make([]bool, c.TypeBool.ValueCount, c.TypeBool.ValueCount)
for i := range c.TypeBool.Value {
if ndr.ReadUint64(b, p, e) != 0 {
c.TypeBool.Value[i] = true
}
}
}
return
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/filetime.go 0000664 0000000 0000000 00000002756 13625372257 0024420 0 ustar 00root root 0000000 0000000 // Package mstypes implements representations of Microsoft types for PAC processing.
package mstypes
import (
"encoding/binary"
"time"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
const unixEpochDiff = 116444736000000000
// FileTime implements the Microsoft FILETIME type https://msdn.microsoft.com/en-us/library/cc230324.aspx
type FileTime struct {
LowDateTime uint32
HighDateTime uint32
}
// Time return a golang Time type from the FileTime
func (ft FileTime) Time() time.Time {
ns := (ft.MSEpoch() - unixEpochDiff) * 100
return time.Unix(0, int64(ns)).UTC()
}
// MSEpoch returns the FileTime as a Microsoft epoch, the number of 100 nano second periods elapsed from January 1, 1601 UTC.
func (ft FileTime) MSEpoch() int64 {
return (int64(ft.HighDateTime) << 32) + int64(ft.LowDateTime)
}
// Unix returns the FileTime as a Unix time, the number of seconds elapsed since January 1, 1970 UTC.
func (ft FileTime) Unix() int64 {
return (ft.MSEpoch() - unixEpochDiff) / 10000000
}
// GetFileTime returns a FileTime type from the provided Golang Time type.
func GetFileTime(t time.Time) FileTime {
ns := t.UnixNano()
fp := (ns / 100) + unixEpochDiff
hd := fp >> 32
ld := fp - (hd << 32)
return FileTime{
LowDateTime: uint32(ld),
HighDateTime: uint32(hd),
}
}
// ReadFileTime reads a FileTime from the bytes slice.
func ReadFileTime(b *[]byte, p *int, e *binary.ByteOrder) FileTime {
l := ndr.ReadUint32(b, p, e)
h := ndr.ReadUint32(b, p, e)
return FileTime{
LowDateTime: l,
HighDateTime: h,
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/filetime_test.go 0000664 0000000 0000000 00000000767 13625372257 0025457 0 ustar 00root root 0000000 0000000 package mstypes
import (
"github.com/stretchr/testify/assert"
"testing"
"time"
)
func TestFileTime(t *testing.T) {
t.Parallel()
//2007-02-22 17:00:01.6382155
tt := time.Date(2007, 2, 22, 17, 0, 1, 638215500, time.UTC)
ft := GetFileTime(tt)
assert.Equal(t, tt.Unix(), ft.Unix(), "Unix epoch time not as expected")
assert.Equal(t, int64(128166372016382155), ft.MSEpoch(), "MSEpoch not as expected")
assert.Equal(t, tt, ft.Time(), "Golang time object returned from FileTime not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/group_membership.go 0000664 0000000 0000000 00000003573 13625372257 0026167 0 ustar 00root root 0000000 0000000 package mstypes
import (
"encoding/binary"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// GroupMembership implements https://msdn.microsoft.com/en-us/library/cc237945.aspx
// RelativeID : A 32-bit unsigned integer that contains the RID of a particular group.
// The possible values for the Attributes flags are identical to those specified in KERB_SID_AND_ATTRIBUTES
type GroupMembership struct {
RelativeID uint32
Attributes uint32
}
// ReadGroupMembership reads a GroupMembership from the bytes slice.
func ReadGroupMembership(b *[]byte, p *int, e *binary.ByteOrder) GroupMembership {
r := ndr.ReadUint32(b, p, e)
a := ndr.ReadUint32(b, p, e)
return GroupMembership{
RelativeID: r,
Attributes: a,
}
}
// DomainGroupMembership implements https://msdn.microsoft.com/en-us/library/hh536344.aspx
// DomainId: A SID structure that contains the SID for the domain.This member is used in conjunction with the GroupIds members to create group SIDs for the device.
// GroupCount: A 32-bit unsigned integer that contains the number of groups within the domain to which the account belongs.
// GroupIds: A pointer to a list of GROUP_MEMBERSHIP structures that contain the groups to which the account belongs in the domain. The number of groups in this list MUST be equal to GroupCount.
type DomainGroupMembership struct {
DomainID RPCSID
GroupCount uint32
GroupIDs []GroupMembership // Size is value of GroupCount
}
// ReadDomainGroupMembership reads a DomainGroupMembership from the bytes slice.
func ReadDomainGroupMembership(b *[]byte, p *int, e *binary.ByteOrder) (DomainGroupMembership, error) {
d, err := ReadRPCSID(b, p, e)
if err != nil {
return DomainGroupMembership{}, err
}
c := ndr.ReadUint32(b, p, e)
g := make([]GroupMembership, c, c)
for i := range g {
g[i] = ReadGroupMembership(b, p, e)
}
return DomainGroupMembership{
DomainID: d,
GroupCount: c,
GroupIDs: g,
}, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/kerb_sid_and_attributes.go 0000664 0000000 0000000 00000002314 13625372257 0027462 0 ustar 00root root 0000000 0000000 package mstypes
import (
"encoding/binary"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// Attributes of a security group membership and can be combined by using the bitwise OR operation.
// They are used by an access check mechanism to specify whether the membership is to be used in an access check decision.
const (
SEGroupMandatory = 31
SEGroupEnabledByDefault = 30
SEGroupEnabled = 29
SEGroupOwner = 28
SEGroupResource = 2
//All other bits MUST be set to zero and MUST be ignored on receipt.
)
// KerbSidAndAttributes implements https://msdn.microsoft.com/en-us/library/cc237947.aspx
type KerbSidAndAttributes struct {
SID RPCSID // A pointer to an RPC_SID structure.
Attributes uint32
}
// ReadKerbSidAndAttributes reads a KerbSidAndAttribute from the bytes slice.
func ReadKerbSidAndAttributes(b *[]byte, p *int, e *binary.ByteOrder) (KerbSidAndAttributes, error) {
s, err := ReadRPCSID(b, p, e)
if err != nil {
return KerbSidAndAttributes{}, err
}
a := ndr.ReadUint32(b, p, e)
return KerbSidAndAttributes{
SID: s,
Attributes: a,
}, nil
}
// SetFlag sets a flag in a uint32 attribute value.
func SetFlag(a *uint32, i uint) {
*a = *a | (1 << (31 - i))
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/rpc_unicode_string.go 0000664 0000000 0000000 00000001775 13625372257 0026502 0 ustar 00root root 0000000 0000000 package mstypes
import (
"encoding/binary"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// RPCUnicodeString implements https://msdn.microsoft.com/en-us/library/cc230365.aspx
type RPCUnicodeString struct {
Length uint16
MaximumLength uint16
BufferPrt uint32
Value string
}
// ReadRPCUnicodeString reads a RPCUnicodeString from the bytes slice.
func ReadRPCUnicodeString(b *[]byte, p *int, e *binary.ByteOrder) (RPCUnicodeString, error) {
l := ndr.ReadUint16(b, p, e)
ml := ndr.ReadUint16(b, p, e)
if ml < l || l%2 != 0 || ml%2 != 0 {
return RPCUnicodeString{}, ndr.Malformed{EText: "Invalid data for RPC_UNICODE_STRING"}
}
ptr := ndr.ReadUint32(b, p, e)
return RPCUnicodeString{
Length: l,
MaximumLength: ml,
BufferPrt: ptr,
}, nil
}
// UnmarshalString populates a golang string into the RPCUnicodeString struct.
func (s *RPCUnicodeString) UnmarshalString(b *[]byte, p *int, e *binary.ByteOrder) (err error) {
s.Value, err = ndr.ReadConformantVaryingString(b, p, e)
return
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/sid.go 0000664 0000000 0000000 00000004222 13625372257 0023367 0 ustar 00root root 0000000 0000000 package mstypes
import (
"encoding/binary"
"encoding/hex"
"fmt"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// RPCSID implements https://msdn.microsoft.com/en-us/library/cc230364.aspx
type RPCSID struct {
Revision uint8
SubAuthorityCount uint8
IdentifierAuthority RPCSIDIdentifierAuthority
SubAuthority []uint32
}
// RPCSIDIdentifierAuthority implements https://msdn.microsoft.com/en-us/library/cc230372.aspx
type RPCSIDIdentifierAuthority struct {
Value []byte // 6 bytes
}
// ReadRPCSID reads a RPC_SID from the bytes slice.
func ReadRPCSID(b *[]byte, p *int, e *binary.ByteOrder) (RPCSID, error) {
size := int(ndr.ReadUint32(b, p, e)) // This is part of the NDR encoding rather than the data type.
r := ndr.ReadUint8(b, p)
if r != uint8(1) {
return RPCSID{}, ndr.Malformed{EText: fmt.Sprintf("SID revision value read as %d when it must be 1", r)}
}
c := ndr.ReadUint8(b, p)
a := ReadRPCSIDIdentifierAuthority(b, p, e)
s := make([]uint32, c, c)
if size != len(s) {
return RPCSID{}, ndr.Malformed{EText: fmt.Sprintf("Number of elements (%d) within SID in the byte stream does not equal the SubAuthorityCount (%d)", size, c)}
}
for i := 0; i < len(s); i++ {
s[i] = ndr.ReadUint32(b, p, e)
}
return RPCSID{
Revision: r,
SubAuthorityCount: c,
IdentifierAuthority: a,
SubAuthority: s,
}, nil
}
// ReadRPCSIDIdentifierAuthority reads a RPC_SIDIdentifierAuthority from the bytes slice.
func ReadRPCSIDIdentifierAuthority(b *[]byte, p *int, e *binary.ByteOrder) RPCSIDIdentifierAuthority {
return RPCSIDIdentifierAuthority{
Value: ndr.ReadBytes(b, p, 6, e),
}
}
// ToString returns the string representation of the RPC_SID.
func (s *RPCSID) ToString() string {
var str string
b := append(make([]byte, 2, 2), s.IdentifierAuthority.Value...)
// For a strange reason this is read big endian: https://msdn.microsoft.com/en-us/library/dd302645.aspx
i := binary.BigEndian.Uint64(b)
if i >= 4294967296 {
str = fmt.Sprintf("S-1-0x%s", hex.EncodeToString(s.IdentifierAuthority.Value))
} else {
str = fmt.Sprintf("S-1-%d", i)
}
for _, sub := range s.SubAuthority {
str = fmt.Sprintf("%s-%d", str, sub)
}
return str
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/user_session_key.go 0000664 0000000 0000000 00000001265 13625372257 0026205 0 ustar 00root root 0000000 0000000 package mstypes
import (
"encoding/binary"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// CypherBlock implements https://msdn.microsoft.com/en-us/library/cc237040.aspx
type CypherBlock struct {
Data []byte // size = 8
}
// UserSessionKey implements https://msdn.microsoft.com/en-us/library/cc237080.aspx
type UserSessionKey struct {
Data []CypherBlock // size = 2
}
// ReadUserSessionKey reads a UserSessionKey from the bytes slice.
func ReadUserSessionKey(b *[]byte, p *int, e *binary.ByteOrder) UserSessionKey {
cb1 := CypherBlock{
Data: ndr.ReadBytes(b, p, 8, e),
}
cb2 := CypherBlock{
Data: ndr.ReadBytes(b, p, 8, e),
}
return UserSessionKey{
Data: []CypherBlock{cb1, cb2},
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/ndr/ 0000775 0000000 0000000 00000000000 13625372257 0021340 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/ndr/error.go 0000664 0000000 0000000 00000000462 13625372257 0023022 0 ustar 00root root 0000000 0000000 package ndr
import "fmt"
// Malformed implements the error interface for malformed NDR encoding errors.
type Malformed struct {
EText string
}
// Error implements the error interface on the Malformed struct.
func (e Malformed) Error() string {
return fmt.Sprintf("Malformed NDR steam: %s", e.EText)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/ndr/ndr.go 0000664 0000000 0000000 00000014502 13625372257 0022454 0 ustar 00root root 0000000 0000000 // Package ndr is DEPRECATED and will be removed from next major revision of gokrb5. Please use gopkg.in/jcmturner/rpc.vX instead. This package is a partial implementation of NDR encoding: http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm
package ndr
import (
"bytes"
"encoding/binary"
"fmt"
"math"
)
const (
protocolVersion = 1
commonHeaderBytes = 8
privateHeaderBytes = 8
bigEndian = 0
littleEndian = 1
ascii = 0
ebcdic = 1
ieee = 0
vax = 1
cray = 2
ibm = 3
)
// CommonHeader implements the NDR common header: https://msdn.microsoft.com/en-us/library/cc243889.aspx
type CommonHeader struct {
Version uint8
Endianness binary.ByteOrder
CharacterEncoding uint8
//FloatRepresentation uint8
HeaderLength uint16
Filler []byte
}
// PrivateHeader implements the NDR private header: https://msdn.microsoft.com/en-us/library/cc243919.aspx
type PrivateHeader struct {
ObjectBufferLength uint32
Filler []byte
}
// ReadHeaders processes the bytes to return the NDR Common and Private headers.
func ReadHeaders(b *[]byte) (CommonHeader, PrivateHeader, int, error) {
ch, p, err := GetCommonHeader(b)
if err != nil {
return CommonHeader{}, PrivateHeader{}, 0, err
}
ph, err := GetPrivateHeader(b, &p, &ch.Endianness)
if err != nil {
return CommonHeader{}, PrivateHeader{}, 0, err
}
return ch, ph, p, err
}
// GetCommonHeader processes the bytes to return the NDR Common header.
func GetCommonHeader(b *[]byte) (CommonHeader, int, error) {
//The first 8 bytes comprise the Common RPC Header for type marshalling.
if len(*b) < commonHeaderBytes {
return CommonHeader{}, 0, Malformed{EText: "Not enough bytes."}
}
if (*b)[0] != protocolVersion {
return CommonHeader{}, 0, Malformed{EText: fmt.Sprintf("Stream does not indicate a RPC Type serialization of version %v", protocolVersion)}
}
endian := int((*b)[1] >> 4 & 0xF)
if endian != 0 && endian != 1 {
return CommonHeader{}, 1, Malformed{EText: "Common header does not indicate a valid endianness"}
}
charEncoding := uint8((*b)[1] & 0xF)
if charEncoding != 0 && charEncoding != 1 {
return CommonHeader{}, 1, Malformed{EText: "Common header does not indicate a valid charater encoding"}
}
var bo binary.ByteOrder
switch endian {
case littleEndian:
bo = binary.LittleEndian
case bigEndian:
bo = binary.BigEndian
}
l := bo.Uint16((*b)[2:4])
if l != commonHeaderBytes {
return CommonHeader{}, 4, Malformed{EText: fmt.Sprintf("Common header does not indicate a valid length: %v instead of %v", uint8((*b)[3]), commonHeaderBytes)}
}
return CommonHeader{
Version: uint8((*b)[0]),
Endianness: bo,
CharacterEncoding: charEncoding,
//FloatRepresentation: uint8(b[2]),
HeaderLength: l,
Filler: (*b)[4:8],
}, 8, nil
}
// GetPrivateHeader processes the bytes to return the NDR Private header.
func GetPrivateHeader(b *[]byte, p *int, bo *binary.ByteOrder) (PrivateHeader, error) {
//The next 8 bytes comprise the RPC type marshalling private header for constructed types.
if len(*b) < (privateHeaderBytes) {
return PrivateHeader{}, Malformed{EText: "Not enough bytes."}
}
var l uint32
buf := bytes.NewBuffer((*b)[*p : *p+4])
binary.Read(buf, *bo, &l)
if l%8 != 0 {
return PrivateHeader{}, Malformed{EText: "Object buffer length not a multiple of 8"}
}
*p += 8
return PrivateHeader{
ObjectBufferLength: l,
Filler: (*b)[4:8],
}, nil
}
// ReadUint8 reads bytes representing a thirty two bit integer.
func ReadUint8(b *[]byte, p *int) (i uint8) {
if len((*b)[*p:]) < 1 {
return
}
ensureAlignment(p, 1)
i = uint8((*b)[*p])
*p++
return
}
// ReadUint16 reads bytes representing a thirty two bit integer.
func ReadUint16(b *[]byte, p *int, e *binary.ByteOrder) (i uint16) {
if len((*b)[*p:]) < 2 {
return
}
ensureAlignment(p, 2)
i = (*e).Uint16((*b)[*p : *p+2])
*p += 2
return
}
// ReadUint32 reads bytes representing a thirty two bit integer.
func ReadUint32(b *[]byte, p *int, e *binary.ByteOrder) (i uint32) {
if len((*b)[*p:]) < 4 {
return
}
ensureAlignment(p, 4)
i = (*e).Uint32((*b)[*p : *p+4])
*p += 4
return
}
// ReadUint64 reads bytes representing a thirty two bit integer.
func ReadUint64(b *[]byte, p *int, e *binary.ByteOrder) (i uint64) {
if len((*b)[*p:]) < 8 {
return
}
ensureAlignment(p, 8)
i = (*e).Uint64((*b)[*p : *p+8])
*p += 8
return
}
// ReadBytes reads the number of bytes specified.
func ReadBytes(b *[]byte, p *int, s int, e *binary.ByteOrder) (r []byte) {
if len((*b)[*p:]) < s {
return
}
buf := bytes.NewBuffer((*b)[*p : *p+s])
r = make([]byte, s)
binary.Read(buf, *e, &r)
*p += s
return r
}
// ReadBool reads bytes representing a boolean.
func ReadBool(b *[]byte, p *int) bool {
if len((*b)[*p:]) < 1 {
return false
}
if ReadUint8(b, p) != 0 {
return true
}
return false
}
// ReadIEEEfloat32 reads bytes representing a IEEE formatted 32 bit float.
func ReadIEEEfloat32(b *[]byte, p *int, e *binary.ByteOrder) float32 {
ensureAlignment(p, 4)
return math.Float32frombits(ReadUint32(b, p, e))
}
// ReadIEEEfloat64 reads bytes representing a IEEE formatted 64 bit float.
func ReadIEEEfloat64(b *[]byte, p *int, e *binary.ByteOrder) float64 {
ensureAlignment(p, 8)
return math.Float64frombits(ReadUint64(b, p, e))
}
// ReadConformantVaryingString reads a Conformant and Varying String from the bytes slice.
func ReadConformantVaryingString(b *[]byte, p *int, e *binary.ByteOrder) (string, error) {
m := ReadUint32(b, p, e) // Max element count
o := ReadUint32(b, p, e) // Offset
a := ReadUint32(b, p, e) // Actual count
if a > (m-o) || o > m {
return "", Malformed{EText: fmt.Sprintf("Not enough bytes. Max: %d, Offset: %d, Actual: %d", m, o, a)}
}
//Unicode string so each element is 2 bytes
//move position based on the offset
if o > 0 {
*p += int(o * 2)
}
s := make([]rune, a, a)
for i := 0; i < len(s); i++ {
s[i] = rune(ReadUint16(b, p, e))
}
ensureAlignment(p, 4)
return string(s), nil
}
// ReadUniDimensionalConformantArrayHeader reads a UniDimensionalConformantArrayHeader from the bytes slice.
func ReadUniDimensionalConformantArrayHeader(b *[]byte, p *int, e *binary.ByteOrder) int {
return int(ReadUint32(b, p, e))
}
func ensureAlignment(p *int, byteSize int) {
if byteSize > 0 {
if s := *p % byteSize; s != 0 {
*p += byteSize - s
}
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/ 0000775 0000000 0000000 00000000000 13625372257 0021320 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/client_claims.go 0000664 0000000 0000000 00000001754 13625372257 0024464 0 ustar 00root root 0000000 0000000 package pac
import (
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/mstypes"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// Claims reference: https://msdn.microsoft.com/en-us/library/hh553895.aspx
// ClientClaimsInfo implements https://msdn.microsoft.com/en-us/library/hh536365.aspx
type ClientClaimsInfo struct {
Claims mstypes.ClaimsSetMetadata
}
// Unmarshal bytes into the ClientClaimsInfo struct
func (k *ClientClaimsInfo) Unmarshal(b []byte) error {
ch, _, p, err := ndr.ReadHeaders(&b)
if err != nil {
return fmt.Errorf("error parsing byte stream headers of CLIENT_CLAIMS_INFO: %v", err)
}
e := &ch.Endianness
//The next 4 bytes are an RPC unique pointer referent. We just skip these
p += 4
k.Claims, err = mstypes.ReadClaimsSetMetadata(&b, &p, e)
if err != nil {
return err
}
//Check that there is only zero padding left
if len(b) >= p {
for _, v := range b[p:] {
if v != 0 {
return ndr.Malformed{EText: "non-zero padding left over at end of data stream"}
}
}
}
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/client_claims_test.go 0000664 0000000 0000000 00000020265 13625372257 0025521 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/mstypes"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
const (
ClaimsEntryIDStr = "ad://ext/sAMAccountName:88d5d9085ea5c0c0"
ClaimsEntryValueStr = "testuser1"
ClaimsEntryIDInt64 = "ad://ext/msDS-SupportedE:88d5dea8f1af5f19"
ClaimsEntryValueInt64 int64 = 28
ClaimsEntryIDUInt64 = "ad://ext/objectClass:88d5de791e7b27e6"
)
func TestPAC_ClientClaimsInfoStr_Unmarshal(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfoStr"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k ClientClaimsInfo
err = k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrayCount, "claims array count not as expected")
assert.Equal(t, mstypes.ClaimsSourceTypeAD, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected")
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsCount, "claims count not as expected")
assert.Equal(t, uint16(3), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].Type, "claims entry type not as expected")
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeString.ValueCount, "claims value count not as expected")
assert.Equal(t, ClaimsEntryIDStr, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].ID, "claims entry ID not as expected")
assert.Equal(t, []string{ClaimsEntryValueStr}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeString.Value, "claims value not as expected")
assert.Equal(t, mstypes.CompressionFormatNone, k.Claims.CompressionFormat, "compression format not as expected")
}
func TestPAC_ClientClaimsMultiValueUint_Unmarshal(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfoMultiUint"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k ClientClaimsInfo
err = k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrayCount, "claims array count not as expected")
assert.Equal(t, mstypes.ClaimsSourceTypeAD, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected")
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsCount, "claims count not as expected")
assert.Equal(t, mstypes.ClaimTypeIDUInt64, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].Type, "claims entry type not as expected")
assert.Equal(t, uint32(4), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeUInt64.ValueCount, "claims value count not as expected")
assert.Equal(t, ClaimsEntryIDUInt64, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].ID, "claims entry ID not as expected")
assert.Equal(t, []uint64{655369, 65543, 65542, 65536}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeUInt64.Value, "claims value not as expected")
assert.Equal(t, mstypes.CompressionFormatNone, k.Claims.CompressionFormat, "compression format not as expected")
}
func TestPAC_ClientClaimsInt_Unmarshal(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfoInt"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k ClientClaimsInfo
err = k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrayCount, "claims array count not as expected")
assert.Equal(t, mstypes.ClaimsSourceTypeAD, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected")
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsCount, "claims count not as expected")
assert.Equal(t, mstypes.ClaimTypeIDInt64, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].Type, "claims entry type not as expected")
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeInt64.ValueCount, "claims value count not as expected")
assert.Equal(t, ClaimsEntryIDInt64, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].ID, "claims entry ID not as expected")
assert.Equal(t, []int64{ClaimsEntryValueInt64}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeInt64.Value, "claims value not as expected")
assert.Equal(t, mstypes.CompressionFormatNone, k.Claims.CompressionFormat, "compression format not as expected")
}
func TestPAC_ClientClaimsMultiValueStr_Unmarshal(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfoMultiStr"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k ClientClaimsInfo
err = k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrayCount, "claims array count not as expected")
assert.Equal(t, mstypes.ClaimsSourceTypeAD, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected")
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsCount, "claims count not as expected")
assert.Equal(t, mstypes.ClaimTypeIDString, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].Type, "claims entry type not as expected")
assert.Equal(t, uint32(4), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeString.ValueCount, "claims value count not as expected")
assert.Equal(t, "ad://ext/otherIpPhone:88d5de9f6b4af985", k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].ID, "claims entry ID not as expected")
assert.Equal(t, []string{"str1", "str2", "str3", "str4"}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeString.Value, "claims value not as expected")
assert.Equal(t, mstypes.CompressionFormatNone, k.Claims.CompressionFormat, "compression format not as expected")
}
func TestPAC_ClientClaimsInfoMultiEntry_Unmarshal(t *testing.T) {
// Has an int and a str claim type
t.Parallel()
b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfoMulti"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k ClientClaimsInfo
err = k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrayCount, "claims array count not as expected")
assert.Equal(t, mstypes.ClaimsSourceTypeAD, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected")
assert.Equal(t, uint32(2), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsCount, "claims count not as expected")
assert.Equal(t, uint16(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].Type, "claims entry type not as expected")
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeInt64.ValueCount, "claims value count not as expected")
assert.Equal(t, ClaimsEntryIDInt64, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].ID, "claims entry ID not as expected")
assert.Equal(t, []int64{int64(28)}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeInt64.Value, "claims value not as expected")
assert.Equal(t, uint16(3), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[1].Type, "claims entry type not as expected")
assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[1].TypeString.ValueCount, "claims value count not as expected")
assert.Equal(t, ClaimsEntryIDStr, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[1].ID, "claims entry ID not as expected")
assert.Equal(t, []string{ClaimsEntryValueStr}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[1].TypeString.Value, "claims value not as expected")
assert.Equal(t, mstypes.CompressionFormatNone, k.Claims.CompressionFormat, "compression format not as expected")
}
func TestPAC_ClientClaimsInfo_Unmarshal_UnsupportedCompression(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfo_XPRESS_HUFF"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k ClientClaimsInfo
err = k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
assert.Equal(t, mstypes.CompressionFormatXPressHuff, k.Claims.CompressionFormat, "compression format not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/client_info.go 0000664 0000000 0000000 00000001751 13625372257 0024144 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/binary"
"gopkg.in/jcmturner/gokrb5.v5/mstypes"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// ClientInfo implements https://msdn.microsoft.com/en-us/library/cc237951.aspx
type ClientInfo struct {
ClientID mstypes.FileTime
NameLength uint16
Name string
}
// Unmarshal bytes into the ClientInfo struct
func (k *ClientInfo) Unmarshal(b []byte) error {
//The PAC_CLIENT_INFO structure is a simple structure that is not NDR-encoded.
var p int
var e binary.ByteOrder = binary.LittleEndian
k.ClientID = mstypes.ReadFileTime(&b, &p, &e)
k.NameLength = ndr.ReadUint16(&b, &p, &e)
if len(b[p:]) < int(k.NameLength) {
return ndr.Malformed{EText: "PAC ClientInfo length truncated"}
}
k.Name = ndr.ReadUTF16String(int(k.NameLength), &b, &p, &e)
//Check that there is only zero padding left
if len(b) >= p {
for _, v := range b[p:] {
if v != 0 {
return ndr.Malformed{EText: "non-zero padding left over at end of data stream"}
}
}
}
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/client_info_test.go 0000664 0000000 0000000 00000001337 13625372257 0025203 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/hex"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestPAC_ClientInfo_Unmarshal(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.TestVectors["PAC_Client_Info"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k ClientInfo
err = k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
assert.Equal(t, time.Date(2017, 5, 6, 15, 53, 11, 000000000, time.UTC), k.ClientID.Time(), "Client ID time not as expected.")
assert.Equal(t, uint16(18), k.NameLength, "Client name length not as expected")
assert.Equal(t, "testuser1", k.Name, "Client name not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/credentials_info.go 0000664 0000000 0000000 00000007424 13625372257 0025166 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/binary"
"errors"
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v5/mstypes"
"gopkg.in/jcmturner/gokrb5.v5/types"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// https://msdn.microsoft.com/en-us/library/cc237931.aspx
// CredentialsInfo implements https://msdn.microsoft.com/en-us/library/cc237953.aspx
type CredentialsInfo struct {
Version uint32
EType uint32
PACCredentialDataEncrypted []byte
PACCredentialData CredentialData
}
// Unmarshal bytes into the CredentialsInfo struct
func (c *CredentialsInfo) Unmarshal(b []byte, k types.EncryptionKey) error {
//The CredentialsInfo structure is a simple structure that is not NDR-encoded.
var p int
var e binary.ByteOrder = binary.LittleEndian
c.Version = ndr.ReadUint32(&b, &p, &e)
if c.Version != 0 {
return errors.New("credentials info version is not zero")
}
c.EType = ndr.ReadUint32(&b, &p, &e)
c.PACCredentialDataEncrypted = ndr.ReadBytes(&b, &p, len(b)-p, &e)
err := c.DecryptEncPart(k, &e)
if err != nil {
return fmt.Errorf("error decrypting PAC Credentials Data: %v", err)
}
return nil
}
// DecryptEncPart decrypts the encrypted part of the CredentialsInfo.
func (c *CredentialsInfo) DecryptEncPart(k types.EncryptionKey, e *binary.ByteOrder) error {
if k.KeyType != int32(c.EType) {
return fmt.Errorf("key provided is not the correct type. Type needed: %d, type provided: %d", c.EType, k.KeyType)
}
pt, err := crypto.DecryptMessage(c.PACCredentialDataEncrypted, k, keyusage.KERB_NON_KERB_SALT)
if err != nil {
return err
}
var p int
c.PACCredentialData = ReadPACCredentialData(&pt, &p, e)
return nil
}
// CredentialData implements https://msdn.microsoft.com/en-us/library/cc237952.aspx
type CredentialData struct {
CredentialCount uint32
Credentials []SECPKGSupplementalCred // Size is the value of CredentialCount
}
// ReadPACCredentialData reads a CredentialData from the byte slice.
func ReadPACCredentialData(b *[]byte, p *int, e *binary.ByteOrder) CredentialData {
c := ndr.ReadUint32(b, p, e)
cr := make([]SECPKGSupplementalCred, c, c)
for i := range cr {
cr[i] = ReadSECPKGSupplementalCred(b, p, e)
}
return CredentialData{
CredentialCount: c,
Credentials: cr,
}
}
// SECPKGSupplementalCred implements https://msdn.microsoft.com/en-us/library/cc237956.aspx
type SECPKGSupplementalCred struct {
PackageName mstypes.RPCUnicodeString
CredentialSize uint32
Credentials []uint8 // Is a ptr. Size is the value of CredentialSize
}
// ReadSECPKGSupplementalCred reads a SECPKGSupplementalCred from the byte slice.
func ReadSECPKGSupplementalCred(b *[]byte, p *int, e *binary.ByteOrder) SECPKGSupplementalCred {
n, _ := mstypes.ReadRPCUnicodeString(b, p, e)
cs := ndr.ReadUint32(b, p, e)
c := make([]uint8, cs, cs)
for i := range c {
c[i] = ndr.ReadUint8(b, p)
}
return SECPKGSupplementalCred{
PackageName: n,
CredentialSize: cs,
Credentials: c,
}
}
// NTLMSupplementalCred implements https://msdn.microsoft.com/en-us/library/cc237949.aspx
type NTLMSupplementalCred struct {
Version uint32
Flags uint32
LMPassword []byte
NTPassword []byte
}
// ReadNTLMSupplementalCred reads a NTLMSupplementalCred from the byte slice.
func ReadNTLMSupplementalCred(b *[]byte, p *int, e *binary.ByteOrder) NTLMSupplementalCred {
v := ndr.ReadUint32(b, p, e)
f := ndr.ReadUint32(b, p, e)
l := ndr.ReadBytes(b, p, 16, e)
n := ndr.ReadBytes(b, p, 16, e)
return NTLMSupplementalCred{
Version: v,
Flags: f,
LMPassword: l,
NTPassword: n,
}
}
const (
// NTLMSupCredLMOWF indicates that the LM OWF member is present and valid.
NTLMSupCredLMOWF = 31
// NTLMSupCredNTOWF indicates that the NT OWF member is present and valid.
NTLMSupCredNTOWF = 30
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/device_claims.go 0000664 0000000 0000000 00000001637 13625372257 0024445 0 ustar 00root root 0000000 0000000 package pac
import (
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/mstypes"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// DeviceClaimsInfo implements https://msdn.microsoft.com/en-us/library/hh554226.aspx
type DeviceClaimsInfo struct {
Claims mstypes.ClaimsSetMetadata
}
// Unmarshal bytes into the DeviceClaimsInfo struct
func (k *DeviceClaimsInfo) Unmarshal(b []byte) error {
ch, _, p, err := ndr.ReadHeaders(&b)
if err != nil {
return fmt.Errorf("error parsing byte stream headers of DEVICE_CLAIMS_INFO: %v", err)
}
e := &ch.Endianness
//The next 4 bytes are an RPC unique pointer referent. We just skip these
p += 4
k.Claims, err = mstypes.ReadClaimsSetMetadata(&b, &p, e)
if err != nil {
return err
}
//Check that there is only zero padding left
if len(b) >= p {
for _, v := range b[p:] {
if v != 0 {
return ndr.Malformed{EText: "non-zero padding left over at end of data stream"}
}
}
}
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/device_info.go 0000664 0000000 0000000 00000005144 13625372257 0024125 0 ustar 00root root 0000000 0000000 package pac
import (
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/mstypes"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// DeviceInfo implements https://msdn.microsoft.com/en-us/library/hh536402.aspx
type DeviceInfo struct {
UserID uint32
PrimaryGroupID uint32
AccountDomainID mstypes.RPCSID
AccountGroupCount uint32
AccountGroupIDs []mstypes.GroupMembership
SIDCount uint32
ExtraSIDs []mstypes.KerbSidAndAttributes
DomainGroupCount uint32
DomainGroup []mstypes.DomainGroupMembership
}
// Unmarshal bytes into the DeviceInfo struct
func (k *DeviceInfo) Unmarshal(b []byte) error {
ch, _, p, err := ndr.ReadHeaders(&b)
if err != nil {
return fmt.Errorf("error parsing byte stream headers: %v", err)
}
e := &ch.Endianness
//The next 4 bytes are an RPC unique pointer referent. We just skip these
p += 4
k.UserID = ndr.ReadUint32(&b, &p, e)
k.PrimaryGroupID = ndr.ReadUint32(&b, &p, e)
k.AccountDomainID, err = mstypes.ReadRPCSID(&b, &p, e)
if err != nil {
return err
}
k.AccountGroupCount = ndr.ReadUint32(&b, &p, e)
if k.AccountGroupCount > 0 {
ag := make([]mstypes.GroupMembership, k.AccountGroupCount, k.AccountGroupCount)
for i := range ag {
ag[i] = mstypes.ReadGroupMembership(&b, &p, e)
}
k.AccountGroupIDs = ag
}
k.SIDCount = ndr.ReadUint32(&b, &p, e)
var ah ndr.ConformantArrayHeader
if k.SIDCount > 0 {
ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e)
if ah.MaxCount != int(k.SIDCount) {
return fmt.Errorf("error with size of ExtraSIDs list. expected: %d, Actual: %d", k.SIDCount, ah.MaxCount)
}
es := make([]mstypes.KerbSidAndAttributes, k.SIDCount, k.SIDCount)
attr := make([]uint32, k.SIDCount, k.SIDCount)
ptr := make([]uint32, k.SIDCount, k.SIDCount)
for i := range attr {
ptr[i] = ndr.ReadUint32(&b, &p, e)
attr[i] = ndr.ReadUint32(&b, &p, e)
}
for i := range es {
if ptr[i] != 0 {
s, err := mstypes.ReadRPCSID(&b, &p, e)
es[i] = mstypes.KerbSidAndAttributes{SID: s, Attributes: attr[i]}
if err != nil {
return ndr.Malformed{EText: fmt.Sprintf("could not read ExtraSIDs: %v", err)}
}
}
}
k.ExtraSIDs = es
}
k.DomainGroupCount = ndr.ReadUint32(&b, &p, e)
if k.DomainGroupCount > 0 {
dg := make([]mstypes.DomainGroupMembership, k.DomainGroupCount, k.DomainGroupCount)
for i := range dg {
dg[i], _ = mstypes.ReadDomainGroupMembership(&b, &p, e)
}
k.DomainGroup = dg
}
//Check that there is only zero padding left
if len(b) >= p {
for _, v := range b[p:] {
if v != 0 {
return ndr.Malformed{EText: "non-zero padding left over at end of data stream"}
}
}
}
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/kerb_validation_info.go 0000664 0000000 0000000 00000023201 13625372257 0026015 0 ustar 00root root 0000000 0000000 // Package pac implements Microsoft Privilege Attribute Certificate (PAC) processing.
package pac
import (
"errors"
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/mstypes"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// KERB_VALIDATION_INFO flags.
const (
USERFLAG_GUEST = 31 // Authentication was done via the GUEST account; no password was used.
USERFLAG_NO_ENCRYPTION_AVAILABLE = 30 // No encryption is available.
USERFLAG_LAN_MANAGER_KEY = 28 // LAN Manager key was used for authentication.
USERFLAG_SUB_AUTH = 25 // Sub-authentication used; session key came from the sub-authentication package.
USERFLAG_EXTRA_SIDS = 26 // Indicates that the ExtraSids field is populated and contains additional SIDs.
USERFLAG_MACHINE_ACCOUNT = 24 // Indicates that the account is a machine account.
USERFLAG_DC_NTLM2 = 23 // Indicates that the domain controller understands NTLMv2.
USERFLAG_RESOURCE_GROUPIDS = 22 // Indicates that the ResourceGroupIds field is populated.
USERFLAG_PROFILEPATH = 21 // Indicates that ProfilePath is populated.
USERFLAG_NTLM2_NTCHALLENGERESP = 20 // The NTLMv2 response from the NtChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and session key generation.
USERFLAG_LM2_LMCHALLENGERESP = 19 // The LMv2 response from the LmChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and session key generation.
USERFLAG_AUTH_LMCHALLENGERESP_KEY_NTCHALLENGERESP = 18 // The LMv2 response from the LmChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and the NTLMv2 response from the NtChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used session key generation.
)
// KerbValidationInfo implement https://msdn.microsoft.com/en-us/library/cc237948.aspx
type KerbValidationInfo struct {
LogOnTime mstypes.FileTime
LogOffTime mstypes.FileTime
KickOffTime mstypes.FileTime
PasswordLastSet mstypes.FileTime
PasswordCanChange mstypes.FileTime
PasswordMustChange mstypes.FileTime
EffectiveName mstypes.RPCUnicodeString
FullName mstypes.RPCUnicodeString
LogonScript mstypes.RPCUnicodeString
ProfilePath mstypes.RPCUnicodeString
HomeDirectory mstypes.RPCUnicodeString
HomeDirectoryDrive mstypes.RPCUnicodeString
LogonCount uint16
BadPasswordCount uint16
UserID uint32
PrimaryGroupID uint32
GroupCount uint32
pGroupIDs uint32
GroupIDs []mstypes.GroupMembership
UserFlags uint32
UserSessionKey mstypes.UserSessionKey
LogonServer mstypes.RPCUnicodeString
LogonDomainName mstypes.RPCUnicodeString
pLogonDomainID uint32
LogonDomainID mstypes.RPCSID
Reserved1 []uint32 // Has 2 elements
UserAccountControl uint32
SubAuthStatus uint32
LastSuccessfulILogon mstypes.FileTime
LastFailedILogon mstypes.FileTime
FailedILogonCount uint32
Reserved3 uint32
SIDCount uint32
pExtraSIDs uint32
ExtraSIDs []mstypes.KerbSidAndAttributes
pResourceGroupDomainSID uint32
ResourceGroupDomainSID mstypes.RPCSID
ResourceGroupCount uint32
pResourceGroupIDs uint32
ResourceGroupIDs []mstypes.GroupMembership
}
// Unmarshal bytes into the DeviceInfo struct
func (k *KerbValidationInfo) Unmarshal(b []byte) (err error) {
ch, _, p, err := ndr.ReadHeaders(&b)
if err != nil {
return fmt.Errorf("error parsing byte stream headers: %v", err)
}
e := &ch.Endianness
//The next 4 bytes are an RPC unique pointer referent. We just skip these
p += 4
k.LogOnTime = mstypes.ReadFileTime(&b, &p, e)
k.LogOffTime = mstypes.ReadFileTime(&b, &p, e)
k.KickOffTime = mstypes.ReadFileTime(&b, &p, e)
k.PasswordLastSet = mstypes.ReadFileTime(&b, &p, e)
k.PasswordCanChange = mstypes.ReadFileTime(&b, &p, e)
k.PasswordMustChange = mstypes.ReadFileTime(&b, &p, e)
if k.EffectiveName, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil {
return
}
if k.FullName, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil {
return
}
if k.LogonScript, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil {
return
}
if k.ProfilePath, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil {
return
}
if k.HomeDirectory, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil {
return
}
if k.HomeDirectoryDrive, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil {
return
}
k.LogonCount = ndr.ReadUint16(&b, &p, e)
k.BadPasswordCount = ndr.ReadUint16(&b, &p, e)
k.UserID = ndr.ReadUint32(&b, &p, e)
k.PrimaryGroupID = ndr.ReadUint32(&b, &p, e)
k.GroupCount = ndr.ReadUint32(&b, &p, e)
k.pGroupIDs = ndr.ReadUint32(&b, &p, e)
k.UserFlags = ndr.ReadUint32(&b, &p, e)
k.UserSessionKey = mstypes.ReadUserSessionKey(&b, &p, e)
if k.LogonServer, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil {
return
}
if k.LogonDomainName, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil {
return
}
k.pLogonDomainID = ndr.ReadUint32(&b, &p, e)
k.Reserved1 = []uint32{
ndr.ReadUint32(&b, &p, e),
ndr.ReadUint32(&b, &p, e),
}
k.UserAccountControl = ndr.ReadUint32(&b, &p, e)
k.SubAuthStatus = ndr.ReadUint32(&b, &p, e)
k.LastSuccessfulILogon = mstypes.ReadFileTime(&b, &p, e)
k.LastFailedILogon = mstypes.ReadFileTime(&b, &p, e)
k.FailedILogonCount = ndr.ReadUint32(&b, &p, e)
k.Reserved3 = ndr.ReadUint32(&b, &p, e)
k.SIDCount = ndr.ReadUint32(&b, &p, e)
k.pExtraSIDs = ndr.ReadUint32(&b, &p, e)
k.pResourceGroupDomainSID = ndr.ReadUint32(&b, &p, e)
k.ResourceGroupCount = ndr.ReadUint32(&b, &p, e)
k.pResourceGroupIDs = ndr.ReadUint32(&b, &p, e)
// Populate pointers
if err = k.EffectiveName.UnmarshalString(&b, &p, e); err != nil {
return
}
if err = k.FullName.UnmarshalString(&b, &p, e); err != nil {
return
}
if err = k.LogonScript.UnmarshalString(&b, &p, e); err != nil {
return
}
if err = k.ProfilePath.UnmarshalString(&b, &p, e); err != nil {
return
}
if err = k.HomeDirectory.UnmarshalString(&b, &p, e); err != nil {
return
}
if err = k.HomeDirectoryDrive.UnmarshalString(&b, &p, e); err != nil {
return
}
var ah ndr.ConformantArrayHeader
if k.GroupCount > 0 {
ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e)
if err != nil {
return
}
if ah.MaxCount != int(k.GroupCount) {
err = errors.New("error with size of group list")
return
}
g := make([]mstypes.GroupMembership, k.GroupCount, k.GroupCount)
for i := range g {
g[i] = mstypes.ReadGroupMembership(&b, &p, e)
}
k.GroupIDs = g
}
if err = k.LogonServer.UnmarshalString(&b, &p, e); err != nil {
return
}
if err = k.LogonDomainName.UnmarshalString(&b, &p, e); err != nil {
return
}
if k.pLogonDomainID != 0 {
k.LogonDomainID, err = mstypes.ReadRPCSID(&b, &p, e)
if err != nil {
return fmt.Errorf("error reading LogonDomainID: %v", err)
}
}
if k.SIDCount > 0 {
ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e)
if err != nil {
return
}
if ah.MaxCount != int(k.SIDCount) {
return fmt.Errorf("error with size of ExtraSIDs list. Expected: %d, Actual: %d", k.SIDCount, ah.MaxCount)
}
es := make([]mstypes.KerbSidAndAttributes, k.SIDCount, k.SIDCount)
attr := make([]uint32, k.SIDCount, k.SIDCount)
ptr := make([]uint32, k.SIDCount, k.SIDCount)
for i := range attr {
ptr[i] = ndr.ReadUint32(&b, &p, e)
attr[i] = ndr.ReadUint32(&b, &p, e)
}
for i := range es {
if ptr[i] != 0 {
s, err := mstypes.ReadRPCSID(&b, &p, e)
es[i] = mstypes.KerbSidAndAttributes{SID: s, Attributes: attr[i]}
if err != nil {
return ndr.Malformed{EText: fmt.Sprintf("could not read ExtraSIDs: %v", err)}
}
}
}
k.ExtraSIDs = es
}
if k.pResourceGroupDomainSID != 0 {
k.ResourceGroupDomainSID, err = mstypes.ReadRPCSID(&b, &p, e)
if err != nil {
return err
}
}
if k.ResourceGroupCount > 0 {
ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e)
if err != nil {
return
}
if ah.MaxCount != int(k.ResourceGroupCount) {
return fmt.Errorf("error with size of ResourceGroup list. Expected: %d, Actual: %d", k.ResourceGroupCount, ah.MaxCount)
}
g := make([]mstypes.GroupMembership, k.ResourceGroupCount, k.ResourceGroupCount)
for i := range g {
g[i] = mstypes.ReadGroupMembership(&b, &p, e)
}
k.ResourceGroupIDs = g
}
//Check that there is only zero padding left
if len(b) >= p {
for _, v := range b[p:] {
if v != 0 {
return ndr.Malformed{EText: "non-zero padding left over at end of data stream"}
}
}
}
return nil
}
// GetGroupMembershipSIDs returns a slice of strings containing the group membership SIDs found in the PAC.
func (k *KerbValidationInfo) GetGroupMembershipSIDs() []string {
var g []string
lSID := k.LogonDomainID.ToString()
for i := range k.GroupIDs {
g = append(g, fmt.Sprintf("%s-%d", lSID, k.GroupIDs[i].RelativeID))
}
for _, s := range k.ExtraSIDs {
var exists = false
for _, es := range g {
if es == s.SID.ToString() {
exists = true
break
}
}
if !exists {
g = append(g, s.SID.ToString())
}
}
for _, r := range k.ResourceGroupIDs {
var exists = false
s := fmt.Sprintf("%s-%d", k.ResourceGroupDomainSID.ToString(), r.RelativeID)
for _, es := range g {
if es == s {
exists = true
break
}
}
if !exists {
g = append(g, s)
}
}
return g
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/kerb_validation_info_test.go 0000664 0000000 0000000 00000040111 13625372257 0027053 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/hex"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/mstypes"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestKerbValidationInfo_Unmarshal(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.TestVectors["PAC_Kerb_Validation_Info_MS"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k KerbValidationInfo
err = k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling KerbValidationInfo: %v", err)
}
assert.Equal(t, time.Date(2006, 4, 28, 1, 42, 50, 925640100, time.UTC), k.LogOnTime.Time(), "LogOnTime not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.LogOffTime.Time(), "LogOffTime not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.KickOffTime.Time(), "KickOffTime not as expected")
assert.Equal(t, time.Date(2006, 3, 18, 10, 44, 54, 837147900, time.UTC), k.PasswordLastSet.Time(), "PasswordLastSet not as expected")
assert.Equal(t, time.Date(2006, 3, 19, 10, 44, 54, 837147900, time.UTC), k.PasswordCanChange.Time(), "PasswordCanChange not as expected")
assert.Equal(t, "lzhu", k.EffectiveName.Value, "EffectiveName not as expected")
assert.Equal(t, "Liqiang(Larry) Zhu", k.FullName.Value, "EffectiveName not as expected")
assert.Equal(t, "ntds2.bat", k.LogonScript.Value, "EffectiveName not as expected")
assert.Equal(t, "", k.ProfilePath.Value, "EffectiveName not as expected")
assert.Equal(t, "", k.HomeDirectory.Value, "EffectiveName not as expected")
assert.Equal(t, "", k.HomeDirectoryDrive.Value, "EffectiveName not as expected")
assert.Equal(t, uint32(131088), k.ProfilePath.BufferPrt, "EffectiveName not as expected")
assert.Equal(t, uint32(131092), k.HomeDirectory.BufferPrt, "EffectiveName not as expected")
assert.Equal(t, uint32(131096), k.HomeDirectoryDrive.BufferPrt, "EffectiveName not as expected")
assert.Equal(t, uint16(4180), k.LogonCount, "LogonCount not as expected")
assert.Equal(t, uint16(0), k.BadPasswordCount, "BadPasswordCount not as expected")
assert.Equal(t, uint32(2914711), k.UserID, "UserID not as expected")
assert.Equal(t, uint32(513), k.PrimaryGroupID, "PrimaryGroupID not as expected")
assert.Equal(t, uint32(26), k.GroupCount, "GroupCount not as expected")
assert.Equal(t, uint32(131100), k.pGroupIDs, "pGroupIDs not as expected")
gids := []mstypes.GroupMembership{
{RelativeID: 3392609, Attributes: 7},
{RelativeID: 2999049, Attributes: 7},
{RelativeID: 3322974, Attributes: 7},
{RelativeID: 513, Attributes: 7},
{RelativeID: 2931095, Attributes: 7},
{RelativeID: 3338539, Attributes: 7},
{RelativeID: 3354830, Attributes: 7},
{RelativeID: 3026599, Attributes: 7},
{RelativeID: 3338538, Attributes: 7},
{RelativeID: 2931096, Attributes: 7},
{RelativeID: 3392610, Attributes: 7},
{RelativeID: 3342740, Attributes: 7},
{RelativeID: 3392630, Attributes: 7},
{RelativeID: 3014318, Attributes: 7},
{RelativeID: 2937394, Attributes: 7},
{RelativeID: 3278870, Attributes: 7},
{RelativeID: 3038018, Attributes: 7},
{RelativeID: 3322975, Attributes: 7},
{RelativeID: 3513546, Attributes: 7},
{RelativeID: 2966661, Attributes: 7},
{RelativeID: 3338434, Attributes: 7},
{RelativeID: 3271401, Attributes: 7},
{RelativeID: 3051245, Attributes: 7},
{RelativeID: 3271606, Attributes: 7},
{RelativeID: 3026603, Attributes: 7},
{RelativeID: 3018354, Attributes: 7},
}
assert.Equal(t, gids, k.GroupIDs, "GroupIDs not as expected")
assert.Equal(t, uint32(32), k.UserFlags, "UserFlags not as expected")
assert.Equal(t, mstypes.UserSessionKey{Data: []mstypes.CypherBlock{{Data: make([]byte, 8, 8)}, {Data: make([]byte, 8, 8)}}}, k.UserSessionKey, "UserSessionKey not as expected")
assert.Equal(t, "NTDEV-DC-05", k.LogonServer.Value, "LogonServer not as expected")
assert.Equal(t, "NTDEV", k.LogonDomainName.Value, "LogonDomainName not as expected")
assert.Equal(t, uint32(131112), k.pLogonDomainID, "pLogonDomainID not as expected")
assert.Equal(t, "S-1-5-21-397955417-626881126-188441444", k.LogonDomainID.ToString(), "LogonDomainID not as expected")
assert.Equal(t, uint32(16), k.UserAccountControl, "UserAccountControl not as expected")
assert.Equal(t, uint32(0), k.SubAuthStatus, "SubAuthStatus not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected")
assert.Equal(t, uint32(0), k.FailedILogonCount, "FailedILogonCount not as expected")
assert.Equal(t, uint32(13), k.SIDCount, "SIDCount not as expected")
assert.Equal(t, uint32(131116), k.pExtraSIDs, "SIDCount not as expected")
assert.Equal(t, int(k.SIDCount), len(k.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same")
var es = []struct {
sid string
attr uint32
}{
{"S-1-5-21-773533881-1816936887-355810188-513", uint32(7)},
{"S-1-5-21-397955417-626881126-188441444-3101812", uint32(536870919)},
{"S-1-5-21-397955417-626881126-188441444-3291368", uint32(536870919)},
{"S-1-5-21-397955417-626881126-188441444-3291341", uint32(536870919)},
{"S-1-5-21-397955417-626881126-188441444-3322973", uint32(536870919)},
{"S-1-5-21-397955417-626881126-188441444-3479105", uint32(536870919)},
{"S-1-5-21-397955417-626881126-188441444-3271400", uint32(536870919)},
{"S-1-5-21-397955417-626881126-188441444-3283393", uint32(536870919)},
{"S-1-5-21-397955417-626881126-188441444-3338537", uint32(536870919)},
{"S-1-5-21-397955417-626881126-188441444-3038991", uint32(536870919)},
{"S-1-5-21-397955417-626881126-188441444-3037999", uint32(536870919)},
{"S-1-5-21-397955417-626881126-188441444-3248111", uint32(536870919)},
}
for i, s := range es {
assert.Equal(t, s.sid, k.ExtraSIDs[i].SID.ToString(), "ExtraSID SID value not as epxected")
assert.Equal(t, s.attr, k.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as epxected")
}
assert.Equal(t, uint32(0), k.pResourceGroupDomainSID, "pResourceGroupDomainSID not as expected")
assert.Equal(t, uint8(0), k.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected")
assert.Equal(t, uint32(0), k.pResourceGroupIDs, "pResourceGroupIDs not as expected")
assert.Equal(t, 0, len(k.ResourceGroupIDs), "ResourceGroupIDs not as expected")
b, err = hex.DecodeString(testdata.TestVectors["PAC_Kerb_Validation_Info"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k2 KerbValidationInfo
err = k2.Unmarshal(b)
if err != nil {
t.Fatal("Could not unmarshal KerbValidationInfo")
}
assert.Equal(t, time.Date(2017, 5, 6, 15, 53, 11, 825766900, time.UTC), k2.LogOnTime.Time(), "LogOnTime not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k2.LogOffTime.Time(), "LogOffTime not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k2.KickOffTime.Time(), "KickOffTime not as expected")
assert.Equal(t, time.Date(2017, 5, 6, 7, 23, 8, 968750000, time.UTC), k2.PasswordLastSet.Time(), "PasswordLastSet not as expected")
assert.Equal(t, time.Date(2017, 5, 7, 7, 23, 8, 968750000, time.UTC), k2.PasswordCanChange.Time(), "PasswordCanChange not as expected")
assert.Equal(t, "testuser1", k2.EffectiveName.Value, "EffectiveName not as expected")
assert.Equal(t, "Test1 User1", k2.FullName.Value, "EffectiveName not as expected")
assert.Equal(t, "", k2.LogonScript.Value, "EffectiveName not as expected")
assert.Equal(t, "", k2.ProfilePath.Value, "EffectiveName not as expected")
assert.Equal(t, "", k2.HomeDirectory.Value, "EffectiveName not as expected")
assert.Equal(t, "", k2.HomeDirectoryDrive.Value, "EffectiveName not as expected")
assert.Equal(t, uint32(131088), k2.ProfilePath.BufferPrt, "EffectiveName not as expected")
assert.Equal(t, uint32(131092), k2.HomeDirectory.BufferPrt, "EffectiveName not as expected")
assert.Equal(t, uint32(131096), k2.HomeDirectoryDrive.BufferPrt, "EffectiveName not as expected")
assert.Equal(t, uint16(216), k2.LogonCount, "LogonCount not as expected")
assert.Equal(t, uint16(0), k2.BadPasswordCount, "BadPasswordCount not as expected")
assert.Equal(t, uint32(1105), k2.UserID, "UserID not as expected")
assert.Equal(t, uint32(513), k2.PrimaryGroupID, "PrimaryGroupID not as expected")
assert.Equal(t, uint32(5), k2.GroupCount, "GroupCount not as expected")
assert.Equal(t, uint32(131100), k2.pGroupIDs, "pGroupIDs not as expected")
gids = []mstypes.GroupMembership{
{RelativeID: 513, Attributes: 7},
{RelativeID: 1108, Attributes: 7},
{RelativeID: 1109, Attributes: 7},
{RelativeID: 1115, Attributes: 7},
{RelativeID: 1116, Attributes: 7},
}
assert.Equal(t, gids, k2.GroupIDs, "GroupIDs not as expected")
assert.Equal(t, uint32(32), k2.UserFlags, "UserFlags not as expected")
assert.Equal(t, mstypes.UserSessionKey{Data: []mstypes.CypherBlock{{Data: make([]byte, 8, 8)}, {Data: make([]byte, 8, 8)}}}, k2.UserSessionKey, "UserSessionKey not as expected")
assert.Equal(t, "ADDC", k2.LogonServer.Value, "LogonServer not as expected")
assert.Equal(t, "TEST", k2.LogonDomainName.Value, "LogonDomainName not as expected")
assert.Equal(t, uint32(131112), k2.pLogonDomainID, "pLogonDomainID not as expected")
assert.Equal(t, "S-1-5-21-3167651404-3865080224-2280184895", k2.LogonDomainID.ToString(), "LogonDomainID not as expected")
assert.Equal(t, uint32(528), k2.UserAccountControl, "UserAccountControl not as expected")
assert.Equal(t, uint32(0), k2.SubAuthStatus, "SubAuthStatus not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k2.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k2.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected")
assert.Equal(t, uint32(0), k2.FailedILogonCount, "FailedILogonCount not as expected")
assert.Equal(t, uint32(2), k2.SIDCount, "SIDCount not as expected")
assert.Equal(t, uint32(131116), k2.pExtraSIDs, "SIDCount not as expected")
assert.Equal(t, int(k2.SIDCount), len(k2.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same")
var es2 = []struct {
sid string
attr uint32
}{
{"S-1-5-21-3167651404-3865080224-2280184895-1114", uint32(536870919)},
{"S-1-5-21-3167651404-3865080224-2280184895-1111", uint32(536870919)},
}
for i, s := range es2 {
assert.Equal(t, s.sid, k2.ExtraSIDs[i].SID.ToString(), "ExtraSID SID value not as epxected")
assert.Equal(t, s.attr, k2.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as epxected")
}
assert.Equal(t, uint32(0), k2.pResourceGroupDomainSID, "pResourceGroupDomainSID not as expected")
assert.Equal(t, uint8(0), k2.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected")
assert.Equal(t, uint32(0), k2.pResourceGroupIDs, "pResourceGroupIDs not as expected")
assert.Equal(t, 0, len(k2.ResourceGroupIDs), "ResourceGroupIDs not as expected")
}
func TestKerbValidationInfo_Unmarshal_DomainTrust(t *testing.T) {
b, err := hex.DecodeString(testdata.TestVectors["PAC_Kerb_Validation_Info_Trust"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k KerbValidationInfo
err = k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling KerbValidationInfo: %v", err)
}
assert.Equal(t, time.Date(2017, 10, 14, 12, 03, 41, 52409900, time.UTC), k.LogOnTime.Time(), "LogOnTime not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.LogOffTime.Time(), "LogOffTime not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.KickOffTime.Time(), "KickOffTime not as expected")
assert.Equal(t, time.Date(2017, 10, 10, 20, 42, 56, 220282300, time.UTC), k.PasswordLastSet.Time(), "PasswordLastSet not as expected")
assert.Equal(t, time.Date(2017, 10, 11, 20, 42, 56, 220282300, time.UTC), k.PasswordCanChange.Time(), "PasswordCanChange not as expected")
assert.Equal(t, "testuser1", k.EffectiveName.Value, "EffectiveName not as expected")
assert.Equal(t, "Test1 User1", k.FullName.Value, "EffectiveName not as expected")
assert.Equal(t, "", k.LogonScript.Value, "EffectiveName not as expected")
assert.Equal(t, "", k.ProfilePath.Value, "EffectiveName not as expected")
assert.Equal(t, "", k.HomeDirectory.Value, "EffectiveName not as expected")
assert.Equal(t, "", k.HomeDirectoryDrive.Value, "EffectiveName not as expected")
assert.Equal(t, uint32(131088), k.ProfilePath.BufferPrt, "EffectiveName not as expected")
assert.Equal(t, uint32(131092), k.HomeDirectory.BufferPrt, "EffectiveName not as expected")
assert.Equal(t, uint32(131096), k.HomeDirectoryDrive.BufferPrt, "EffectiveName not as expected")
assert.Equal(t, uint16(46), k.LogonCount, "LogonCount not as expected")
assert.Equal(t, uint16(0), k.BadPasswordCount, "BadPasswordCount not as expected")
assert.Equal(t, uint32(1106), k.UserID, "UserID not as expected")
assert.Equal(t, uint32(513), k.PrimaryGroupID, "PrimaryGroupID not as expected")
assert.Equal(t, uint32(3), k.GroupCount, "GroupCount not as expected")
assert.Equal(t, uint32(131100), k.pGroupIDs, "pGroupIDs not as expected")
gids := []mstypes.GroupMembership{
{RelativeID: 1110, Attributes: 7},
{RelativeID: 513, Attributes: 7},
{RelativeID: 1109, Attributes: 7},
}
assert.Equal(t, gids, k.GroupIDs, "GroupIDs not as expected")
assert.Equal(t, uint32(544), k.UserFlags, "UserFlags not as expected")
assert.Equal(t, mstypes.UserSessionKey{Data: []mstypes.CypherBlock{{Data: make([]byte, 8, 8)}, {Data: make([]byte, 8, 8)}}}, k.UserSessionKey, "UserSessionKey not as expected")
assert.Equal(t, "UDC", k.LogonServer.Value, "LogonServer not as expected")
assert.Equal(t, "USER", k.LogonDomainName.Value, "LogonDomainName not as expected")
assert.Equal(t, uint32(131112), k.pLogonDomainID, "pLogonDomainID not as expected")
assert.Equal(t, "S-1-5-21-2284869408-3503417140-1141177250", k.LogonDomainID.ToString(), "LogonDomainID not as expected")
assert.Equal(t, uint32(528), k.UserAccountControl, "UserAccountControl not as expected")
assert.Equal(t, uint32(0), k.SubAuthStatus, "SubAuthStatus not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected")
assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected")
assert.Equal(t, uint32(0), k.FailedILogonCount, "FailedILogonCount not as expected")
assert.Equal(t, uint32(1), k.SIDCount, "SIDCount not as expected")
assert.Equal(t, uint32(131116), k.pExtraSIDs, "SIDCount not as expected")
assert.Equal(t, int(k.SIDCount), len(k.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same")
var es = []struct {
sid string
attr uint32
}{
{"S-1-18-1", uint32(7)},
}
for i, s := range es {
assert.Equal(t, s.sid, k.ExtraSIDs[i].SID.ToString(), "ExtraSID SID value not as epxected")
assert.Equal(t, s.attr, k.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as epxected")
}
assert.Equal(t, uint32(131124), k.pResourceGroupDomainSID, "pResourceGroupDomainSID not as expected")
assert.Equal(t, uint8(4), k.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected")
assert.Equal(t, "S-1-5-21-3062750306-1230139592-1973306805", k.ResourceGroupDomainSID.ToString(), "ResourceGroupDomainSID value not as expected")
assert.Equal(t, uint32(131128), k.pResourceGroupIDs, "pResourceGroupIDs not as expected")
assert.Equal(t, 2, len(k.ResourceGroupIDs), "ResourceGroupIDs not as expected")
rgids := []mstypes.GroupMembership{
{RelativeID: 1107, Attributes: 536870919},
{RelativeID: 1108, Attributes: 536870919},
}
assert.Equal(t, rgids, k.ResourceGroupIDs, "ResourceGroupIDs not as expected")
groupSids := []string{"S-1-5-21-2284869408-3503417140-1141177250-1110",
"S-1-5-21-2284869408-3503417140-1141177250-513",
"S-1-5-21-2284869408-3503417140-1141177250-1109",
"S-1-18-1",
"S-1-5-21-3062750306-1230139592-1973306805-1107",
"S-1-5-21-3062750306-1230139592-1973306805-1108"}
assert.Equal(t, groupSids, k.GetGroupMembershipSIDs(), "GroupMembershipSIDs not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/pac_info_buffer.go 0000664 0000000 0000000 00000001650 13625372257 0024760 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/binary"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
const (
ulTypeKerbValidationInfo = 1
ulTypeCredentials = 2
ulTypePACServerSignatureData = 6
ulTypePACKDCSignatureData = 7
ulTypePACClientInfo = 10
ulTypeS4UDelegationInfo = 11
ulTypeUPNDNSInfo = 12
ulTypePACClientClaimsInfo = 13
ulTypePACDeviceInfo = 14
ulTypePACDeviceClaimsInfo = 15
)
// InfoBuffer implements the PAC Info Buffer: https://msdn.microsoft.com/en-us/library/cc237954.aspx
type InfoBuffer struct {
ULType uint32
CBBufferSize uint32
Offset uint64
}
// ReadPACInfoBuffer reads a InfoBuffer from the byte slice.
func ReadPACInfoBuffer(b *[]byte, p *int, e *binary.ByteOrder) InfoBuffer {
u := ndr.ReadUint32(b, p, e)
s := ndr.ReadUint32(b, p, e)
o := ndr.ReadUint64(b, p, e)
return InfoBuffer{
ULType: u,
CBBufferSize: s,
Offset: o,
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/pac_type.go 0000664 0000000 0000000 00000014060 13625372257 0023454 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/binary"
"errors"
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/crypto"
"gopkg.in/jcmturner/gokrb5.v5/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v5/types"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// PACType implements: https://msdn.microsoft.com/en-us/library/cc237950.aspx
type PACType struct {
CBuffers uint32
Version uint32
Buffers []InfoBuffer
Data []byte
KerbValidationInfo *KerbValidationInfo
CredentialsInfo *CredentialsInfo
ServerChecksum *SignatureData
KDCChecksum *SignatureData
ClientInfo *ClientInfo
S4UDelegationInfo *S4UDelegationInfo
UPNDNSInfo *UPNDNSInfo
ClientClaimsInfo *ClientClaimsInfo
DeviceInfo *DeviceInfo
DeviceClaimsInfo *DeviceClaimsInfo
ZeroSigData []byte
}
// Unmarshal bytes into the PACType struct
func (pac *PACType) Unmarshal(b []byte) error {
var p int
var e binary.ByteOrder = binary.LittleEndian
pac.Data = b
zb := make([]byte, len(b), len(b))
copy(zb, b)
pac.ZeroSigData = zb
pac.CBuffers = ndr.ReadUint32(&b, &p, &e)
pac.Version = ndr.ReadUint32(&b, &p, &e)
buf := make([]InfoBuffer, pac.CBuffers, pac.CBuffers)
for i := range buf {
buf[i] = ReadPACInfoBuffer(&b, &p, &e)
}
pac.Buffers = buf
return nil
}
// ProcessPACInfoBuffers processes the PAC Info Buffers.
// https://msdn.microsoft.com/en-us/library/cc237954.aspx
func (pac *PACType) ProcessPACInfoBuffers(key types.EncryptionKey) error {
for _, buf := range pac.Buffers {
p := make([]byte, buf.CBBufferSize, buf.CBBufferSize)
copy(p, pac.Data[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)])
switch int(buf.ULType) {
case ulTypeKerbValidationInfo:
if pac.KerbValidationInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k KerbValidationInfo
err := k.Unmarshal(p)
if err != nil {
return fmt.Errorf("error processing KerbValidationInfo: %v", err)
}
pac.KerbValidationInfo = &k
case ulTypeCredentials:
// Currently PAC parsing is only useful on the service side in gokrb5
// The CredentialsInfo are only useful when gokrb5 has implemented RFC4556 and only applied on the client side.
// Skipping CredentialsInfo - will be revisited under RFC4556 implementation.
continue
//if pac.CredentialsInfo != nil {
// //Must ignore subsequent buffers of this type
// continue
//}
//var k CredentialsInfo
//err := k.Unmarshal(p, key) // The encryption key used is the AS reply key only available to the client.
//if err != nil {
// return fmt.Errorf("error processing CredentialsInfo: %v", err)
//}
//pac.CredentialsInfo = &k
case ulTypePACServerSignatureData:
if pac.ServerChecksum != nil {
//Must ignore subsequent buffers of this type
continue
}
var k SignatureData
zb, err := k.Unmarshal(p)
copy(pac.ZeroSigData[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)], zb)
if err != nil {
return fmt.Errorf("error processing ServerChecksum: %v", err)
}
pac.ServerChecksum = &k
case ulTypePACKDCSignatureData:
if pac.KDCChecksum != nil {
//Must ignore subsequent buffers of this type
continue
}
var k SignatureData
zb, err := k.Unmarshal(p)
copy(pac.ZeroSigData[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)], zb)
if err != nil {
return fmt.Errorf("error processing KDCChecksum: %v", err)
}
pac.KDCChecksum = &k
case ulTypePACClientInfo:
if pac.ClientInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k ClientInfo
err := k.Unmarshal(p)
if err != nil {
return fmt.Errorf("error processing ClientInfo: %v", err)
}
pac.ClientInfo = &k
case ulTypeS4UDelegationInfo:
if pac.S4UDelegationInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k S4UDelegationInfo
err := k.Unmarshal(p)
if err != nil {
return fmt.Errorf("error processing S4U_DelegationInfo: %v", err)
}
pac.S4UDelegationInfo = &k
case ulTypeUPNDNSInfo:
if pac.UPNDNSInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k UPNDNSInfo
err := k.Unmarshal(p)
if err != nil {
return fmt.Errorf("error processing UPN_DNSInfo: %v", err)
}
pac.UPNDNSInfo = &k
case ulTypePACClientClaimsInfo:
if pac.ClientClaimsInfo != nil || len(p) < 1 {
//Must ignore subsequent buffers of this type
continue
}
var k ClientClaimsInfo
err := k.Unmarshal(p)
if err != nil {
return fmt.Errorf("error processing ClientClaimsInfo: %v", err)
}
pac.ClientClaimsInfo = &k
case ulTypePACDeviceInfo:
if pac.DeviceInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k DeviceInfo
err := k.Unmarshal(p)
if err != nil {
return fmt.Errorf("error processing DeviceInfo: %v", err)
}
pac.DeviceInfo = &k
case ulTypePACDeviceClaimsInfo:
if pac.DeviceClaimsInfo != nil {
//Must ignore subsequent buffers of this type
continue
}
var k DeviceClaimsInfo
err := k.Unmarshal(p)
if err != nil {
return fmt.Errorf("error processing DeviceClaimsInfo: %v", err)
}
pac.DeviceClaimsInfo = &k
}
}
if ok, err := pac.validate(key); !ok {
return err
}
return nil
}
func (pac *PACType) validate(key types.EncryptionKey) (bool, error) {
if pac.KerbValidationInfo == nil {
return false, errors.New("PAC Info Buffers does not contain a KerbValidationInfo")
}
if pac.ServerChecksum == nil {
return false, errors.New("PAC Info Buffers does not contain a ServerChecksum")
}
if pac.KDCChecksum == nil {
return false, errors.New("PAC Info Buffers does not contain a KDCChecksum")
}
if pac.ClientInfo == nil {
return false, errors.New("PAC Info Buffers does not contain a ClientInfo")
}
etype, err := crypto.GetChksumEtype(int32(pac.ServerChecksum.SignatureType))
if err != nil {
return false, err
}
if ok := etype.VerifyChecksum(key.KeyValue,
pac.ZeroSigData,
pac.ServerChecksum.Signature,
keyusage.KERB_NON_KERB_CKSUM_SALT); !ok {
return false, errors.New("PAC service checksum verification failed")
}
return true, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/pac_type_test.go 0000664 0000000 0000000 00000003071 13625372257 0024513 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/hex"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestPACTypeValidate(t *testing.T) {
t.Parallel()
v := "PAC_AD_WIN2K_PAC"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
var pac PACType
err = pac.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
b, _ = hex.DecodeString(testdata.SYSHTTP_KEYTAB)
kt, _ := keytab.Parse(b)
key, err := kt.GetEncryptionKey([]string{"sysHTTP"}, "TEST.GOKRB5", 2, 18)
if err != nil {
t.Fatalf("Error getting key: %v", err)
}
err = pac.ProcessPACInfoBuffers(key)
if err != nil {
t.Fatalf("Processing reference pac error: %v", err)
}
pacInvalidServerSig := pac
// Check the signature to force failure
pacInvalidServerSig.ServerChecksum.Signature[0] ^= 0xFF
pacInvalidNilKerbValidationInfo := pac
pacInvalidNilKerbValidationInfo.KerbValidationInfo = nil
pacInvalidNilServerSig := pac
pacInvalidNilServerSig.ServerChecksum = nil
pacInvalidNilKdcSig := pac
pacInvalidNilKdcSig.KDCChecksum = nil
pacInvalidClientInfo := pac
pacInvalidClientInfo.ClientInfo = nil
var pacs = []struct {
pac PACType
}{
{pacInvalidServerSig},
{pacInvalidNilKerbValidationInfo},
{pacInvalidNilServerSig},
{pacInvalidNilKdcSig},
{pacInvalidClientInfo},
}
for i, s := range pacs {
v, _ := s.pac.validate(key)
assert.False(t, v, fmt.Sprintf("Validation should have failed for test %v", i))
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/s4u_delegation_info.go 0000664 0000000 0000000 00000003057 13625372257 0025575 0 ustar 00root root 0000000 0000000 package pac
import (
"fmt"
"gopkg.in/jcmturner/gokrb5.v5/mstypes"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// S4UDelegationInfo implements https://msdn.microsoft.com/en-us/library/cc237944.aspx
type S4UDelegationInfo struct {
S4U2proxyTarget mstypes.RPCUnicodeString // The name of the principal to whom the application can forward the ticket.
TransitedListSize uint32
S4UTransitedServices []mstypes.RPCUnicodeString // List of all services that have been delegated through by this client and subsequent services or servers.. Size is value of TransitedListSize
}
// Unmarshal bytes into the S4UDelegationInfo struct
func (k *S4UDelegationInfo) Unmarshal(b []byte) error {
ch, _, p, err := ndr.ReadHeaders(&b)
if err != nil {
return fmt.Errorf("error parsing byte stream headers: %v", err)
}
e := &ch.Endianness
//The next 4 bytes are an RPC unique pointer referent. We just skip these
p += 4
k.S4U2proxyTarget, err = mstypes.ReadRPCUnicodeString(&b, &p, e)
if err != nil {
return err
}
k.TransitedListSize = ndr.ReadUint32(&b, &p, e)
if k.TransitedListSize > 0 {
ts := make([]mstypes.RPCUnicodeString, k.TransitedListSize, k.TransitedListSize)
for i := range ts {
ts[i], err = mstypes.ReadRPCUnicodeString(&b, &p, e)
if err != nil {
return err
}
}
for i := range ts {
ts[i].UnmarshalString(&b, &p, e)
}
k.S4UTransitedServices = ts
}
//Check that there is only zero padding left
for _, v := range b[p:] {
if v != 0 {
return ndr.Malformed{EText: "non-zero padding left over at end of data stream"}
}
}
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/signature_data.go 0000664 0000000 0000000 00000002401 13625372257 0024636 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/binary"
"gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
/*
https://msdn.microsoft.com/en-us/library/cc237955.aspx
*/
// SignatureData implements https://msdn.microsoft.com/en-us/library/cc237955.aspx
type SignatureData struct {
SignatureType uint32
Signature []byte
RODCIdentifier uint16
}
// Unmarshal bytes into the SignatureData struct
func (k *SignatureData) Unmarshal(b []byte) ([]byte, error) {
var p int
var e binary.ByteOrder = binary.LittleEndian
k.SignatureType = ndr.ReadUint32(&b, &p, &e)
var c int
switch k.SignatureType {
case chksumtype.KERB_CHECKSUM_HMAC_MD5_UNSIGNED:
c = 16
case uint32(chksumtype.HMAC_SHA1_96_AES128):
c = 12
case uint32(chksumtype.HMAC_SHA1_96_AES256):
c = 12
}
sp := p
k.Signature = ndr.ReadBytes(&b, &p, c, &e)
k.RODCIdentifier = ndr.ReadUint16(&b, &p, &e)
//Check that there is only zero padding left
for _, v := range b[p:] {
if v != 0 {
return []byte{}, ndr.Malformed{EText: "non-zero padding left over at end of data stream"}
}
}
// Create bytes with zeroed signature needed for checksum verification
rb := make([]byte, len(b), len(b))
copy(rb, b)
z := make([]byte, len(b), len(b))
copy(rb[sp:sp+c], z)
return rb, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/signature_data_test.go 0000664 0000000 0000000 00000003467 13625372257 0025712 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestPAC_SignatureData_Unmarshal_Server_Signature(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.TestVectors["PAC_Server_Signature"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k SignatureData
bz, err := k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
sig, _ := hex.DecodeString("1e251d98d552be7df384f550")
zeroed, _ := hex.DecodeString("10000000000000000000000000000000")
assert.Equal(t, uint32(chksumtype.HMAC_SHA1_96_AES256), k.SignatureType, "Server signature type not as expected")
assert.Equal(t, sig, k.Signature, "Server signature not as expected")
assert.Equal(t, uint16(0), k.RODCIdentifier, "RODC Identifier not as expected")
assert.Equal(t, zeroed, bz, "Returned bytes with zeroed signature not as expected")
}
func TestPAC_SignatureData_Unmarshal_KDC_Signature(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.TestVectors["PAC_KDC_Signature"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k SignatureData
bz, err := k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
sig, _ := hex.DecodeString("340be28b48765d0519ee9346cf53d822")
zeroed, _ := hex.DecodeString("76ffffff00000000000000000000000000000000")
assert.Equal(t, chksumtype.KERB_CHECKSUM_HMAC_MD5_UNSIGNED, k.SignatureType, "Server signature type not as expected")
assert.Equal(t, sig, k.Signature, "Server signature not as expected")
assert.Equal(t, uint16(0), k.RODCIdentifier, "RODC Identifier not as expected")
assert.Equal(t, zeroed, bz, "Returned bytes with zeroed signature not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/upn_dns_info.go 0000664 0000000 0000000 00000003202 13625372257 0024325 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/binary"
"sort"
"gopkg.in/jcmturner/rpc.v0/ndr"
)
// UPNDNSInfo implements https://msdn.microsoft.com/en-us/library/dd240468.aspx
type UPNDNSInfo struct {
UPNLength uint16
UPNOffset uint16
DNSDomainNameLength uint16
DNSDomainNameOffset uint16
Flags uint32
UPN string
DNSDomain string
}
const (
upnNoUPNAttr = 31
)
// Unmarshal bytes into the UPN_DNSInfo struct
func (k *UPNDNSInfo) Unmarshal(b []byte) error {
//The UPN_DNS_INFO structure is a simple structure that is not NDR-encoded.
var p int
var e binary.ByteOrder = binary.LittleEndian
k.UPNLength = ndr.ReadUint16(&b, &p, &e)
k.UPNOffset = ndr.ReadUint16(&b, &p, &e)
k.DNSDomainNameLength = ndr.ReadUint16(&b, &p, &e)
k.DNSDomainNameOffset = ndr.ReadUint16(&b, &p, &e)
k.Flags = ndr.ReadUint32(&b, &p, &e)
ub := b[k.UPNOffset : k.UPNOffset+k.UPNLength]
db := b[k.DNSDomainNameOffset : k.DNSDomainNameOffset+k.DNSDomainNameLength]
u := make([]rune, k.UPNLength/2, k.UPNLength/2)
for i := 0; i < len(u); i++ {
q := i * 2
u[i] = rune(ndr.ReadUint16(&ub, &q, &e))
}
k.UPN = string(u)
d := make([]rune, k.DNSDomainNameLength/2, k.DNSDomainNameLength/2)
for i := 0; i < len(d); i++ {
q := i * 2
d[i] = rune(ndr.ReadUint16(&db, &q, &e))
}
k.DNSDomain = string(d)
l := []int{
p,
int(k.UPNOffset + k.UPNLength),
int(k.DNSDomainNameOffset + k.DNSDomainNameLength),
}
sort.Ints(l)
//Check that there is only zero padding left
for _, v := range b[l[2]:] {
if v != 0 {
return ndr.Malformed{EText: "non-zero padding left over at end of data stream."}
}
}
return nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/upn_dns_info_test.go 0000664 0000000 0000000 00000001730 13625372257 0025370 0 ustar 00root root 0000000 0000000 package pac
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestUPN_DNSInfo_Unmarshal(t *testing.T) {
t.Parallel()
b, err := hex.DecodeString(testdata.TestVectors["PAC_UPN_DNS_Info"])
if err != nil {
t.Fatal("Could not decode test data hex string")
}
var k UPNDNSInfo
err = k.Unmarshal(b)
if err != nil {
t.Fatalf("Error unmarshaling test data: %v", err)
}
assert.Equal(t, uint16(42), k.UPNLength, "UPN Length not as expected")
assert.Equal(t, uint16(16), k.UPNOffset, "UPN Offset not as expected")
assert.Equal(t, uint16(22), k.DNSDomainNameLength, "DNS Domain Length not as expected")
assert.Equal(t, uint16(64), k.DNSDomainNameOffset, "DNS Domain Offset not as expected")
assert.Equal(t, "testuser1@test.gokrb5", k.UPN, "UPN not as expected")
assert.Equal(t, "TEST.GOKRB5", k.DNSDomain, "DNS Domain not as expected")
assert.Equal(t, uint32(0), k.Flags, "DNS Domain not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/ 0000775 0000000 0000000 00000000000 13625372257 0022215 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/APExchange.go 0000664 0000000 0000000 00000011045 13625372257 0024510 0 ustar 00root root 0000000 0000000 package service
import (
"fmt"
"time"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/iana/errorcode"
"gopkg.in/jcmturner/gokrb5.v5/iana/flags"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/krberror"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
// ValidateAPREQ validates an AP_REQ sent to the service. Returns a boolean for if the AP_REQ is valid and the client's principal name and realm.
func ValidateAPREQ(APReq messages.APReq, kt keytab.Keytab, sa string, cAddr string, requireHostAddr bool) (bool, credentials.Credentials, error) {
var creds credentials.Credentials
err := APReq.Ticket.DecryptEncPart(kt, sa)
if err != nil {
return false, creds, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of service ticket provided")
}
a, err := APReq.DecryptAuthenticator(APReq.Ticket.DecryptedEncPart.Key)
if err != nil {
return false, creds, krberror.Errorf(err, krberror.DecryptingError, "error extracting authenticator")
}
// Check CName in Authenticator is the same as that in the ticket
if !a.CName.Equal(APReq.Ticket.DecryptedEncPart.CName) {
err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADMATCH, "CName in Authenticator does not match that in service ticket")
return false, creds, err
}
if len(APReq.Ticket.DecryptedEncPart.CAddr) > 0 {
h, err := types.GetHostAddress(cAddr)
if err != nil {
err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, err.Error())
return false, creds, err
}
if !types.HostAddressesContains(APReq.Ticket.DecryptedEncPart.CAddr, h) {
err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "Client address not within the list contained in the service ticket")
return false, creds, err
}
} else if requireHostAddr {
err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "ticket does not contain HostAddress values required")
return false, creds, err
}
// Check the clock skew between the client and the service server
ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond)
t := time.Now().UTC()
// Hardcode 5 min max skew. May want to make this configurable
d := time.Duration(5) * time.Minute
if t.Sub(ct) > d || ct.Sub(t) > d {
err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_SKEW, fmt.Sprintf("Clock skew with client too large. Greater than %v seconds", d))
return false, creds, err
}
// Check for replay
rc := GetReplayCache(d)
if rc.IsReplay(APReq.Ticket.SName, a) {
err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_REPEAT, "Replay detected")
return false, creds, err
}
// Check for future tickets or invalid tickets
if APReq.Ticket.DecryptedEncPart.StartTime.Sub(t) > d || types.IsFlagSet(&APReq.Ticket.DecryptedEncPart.Flags, flags.Invalid) {
err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_TKT_NYV, "Service ticket provided is not yet valid")
return false, creds, err
}
// Check for expired ticket
if t.Sub(APReq.Ticket.DecryptedEncPart.EndTime) > d {
err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_TKT_EXPIRED, "Service ticket provided has expired")
return false, creds, err
}
creds = credentials.NewCredentialsFromPrincipal(a.CName, a.CRealm)
creds.SetAuthTime(t)
creds.SetAuthenticated(true)
creds.SetValidUntil(APReq.Ticket.DecryptedEncPart.EndTime)
isPAC, pac, err := APReq.Ticket.GetPACType(kt, sa)
if isPAC && err != nil {
return false, creds, err
}
if isPAC {
// There is a valid PAC. Adding attributes to creds
creds.SetADCredentials(credentials.ADCredentials{
GroupMembershipSIDs: pac.KerbValidationInfo.GetGroupMembershipSIDs(),
LogOnTime: pac.KerbValidationInfo.LogOnTime.Time(),
LogOffTime: pac.KerbValidationInfo.LogOffTime.Time(),
PasswordLastSet: pac.KerbValidationInfo.PasswordLastSet.Time(),
EffectiveName: pac.KerbValidationInfo.EffectiveName.Value,
FullName: pac.KerbValidationInfo.FullName.Value,
UserID: int(pac.KerbValidationInfo.UserID),
PrimaryGroupID: int(pac.KerbValidationInfo.PrimaryGroupID),
LogonServer: pac.KerbValidationInfo.LogonServer.Value,
LogonDomainName: pac.KerbValidationInfo.LogonDomainName.Value,
LogonDomainID: pac.KerbValidationInfo.LogonDomainID.ToString(),
})
}
return true, creds, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/APExchange_test.go 0000664 0000000 0000000 00000022457 13625372257 0025560 0 ustar 00root root 0000000 0000000 package service
import (
"encoding/hex"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/client"
"gopkg.in/jcmturner/gokrb5.v5/config"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/iana/errorcode"
"gopkg.in/jcmturner/gokrb5.v5/iana/flags"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
func TestValidateAPREQ(t *testing.T) {
t.Parallel()
cl := getClient()
sname := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
st := time.Now().UTC()
tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
types.NewKrbFlags(),
kt,
18,
1,
st,
st,
st.Add(time.Duration(24)*time.Hour),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
APReq, err := messages.NewAPReq(
tkt,
sessionKey,
newTestAuthenticator(*cl.Credentials),
)
if err != nil {
t.Fatalf("Error getting test AP_REQ: %v", err)
}
ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false)
if !ok || err != nil {
t.Fatalf("Validation of AP_REQ failed when it should not have: %v", err)
}
}
func TestValidateAPREQ_KRB_AP_ERR_BADMATCH(t *testing.T) {
t.Parallel()
cl := getClient()
sname := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
st := time.Now().UTC()
tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
types.NewKrbFlags(),
kt,
18,
1,
st,
st,
st.Add(time.Duration(24)*time.Hour),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
a := newTestAuthenticator(*cl.Credentials)
a.CName = types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"BADMATCH"},
}
APReq, err := messages.NewAPReq(
tkt,
sessionKey,
a,
)
if err != nil {
t.Fatalf("Error getting test AP_REQ: %v", err)
}
ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false)
if ok || err == nil {
t.Fatal("Validation of AP_REQ passed when it should not have")
}
if _, ok := err.(messages.KRBError); ok {
assert.Equal(t, errorcode.KRB_AP_ERR_BADMATCH, err.(messages.KRBError).ErrorCode, "Error code not as expected")
} else {
t.Fatalf("Error is not a KRBError: %v", err)
}
}
func TestValidateAPREQ_LargeClockSkew(t *testing.T) {
t.Parallel()
cl := getClient()
sname := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
st := time.Now().UTC()
tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
types.NewKrbFlags(),
kt,
18,
1,
st,
st,
st.Add(time.Duration(24)*time.Hour),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
a := newTestAuthenticator(*cl.Credentials)
a.CTime = a.CTime.Add(time.Duration(-10) * time.Minute)
APReq, err := messages.NewAPReq(
tkt,
sessionKey,
a,
)
if err != nil {
t.Fatalf("Error getting test AP_REQ: %v", err)
}
ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false)
if ok || err == nil {
t.Fatal("Validation of AP_REQ passed when it should not have")
}
if _, ok := err.(messages.KRBError); ok {
assert.Equal(t, errorcode.KRB_AP_ERR_SKEW, err.(messages.KRBError).ErrorCode, "Error code not as expected")
} else {
t.Fatalf("Error is not a KRBError: %v", err)
}
}
func TestValidateAPREQ_Replay(t *testing.T) {
t.Parallel()
cl := getClient()
sname := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
st := time.Now().UTC()
tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
types.NewKrbFlags(),
kt,
18,
1,
st,
st,
st.Add(time.Duration(24)*time.Hour),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
APReq, err := messages.NewAPReq(
tkt,
sessionKey,
newTestAuthenticator(*cl.Credentials),
)
if err != nil {
t.Fatalf("Error getting test AP_REQ: %v", err)
}
ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false)
if !ok || err != nil {
t.Fatalf("Validation of AP_REQ failed when it should not have: %v", err)
}
// Replay
ok, _, err = ValidateAPREQ(APReq, kt, "", "127.0.0.1", false)
if ok || err == nil {
t.Fatal("Validation of AP_REQ passed when it should not have")
}
assert.IsType(t, messages.KRBError{}, err, "Error is not a KRBError")
assert.Equal(t, errorcode.KRB_AP_ERR_REPEAT, err.(messages.KRBError).ErrorCode, "Error code not as expected")
}
func TestValidateAPREQ_FutureTicket(t *testing.T) {
t.Parallel()
cl := getClient()
sname := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
st := time.Now().UTC()
tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
types.NewKrbFlags(),
kt,
18,
1,
st,
st.Add(time.Duration(60)*time.Minute),
st.Add(time.Duration(24)*time.Hour),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
a := newTestAuthenticator(*cl.Credentials)
APReq, err := messages.NewAPReq(
tkt,
sessionKey,
a,
)
if err != nil {
t.Fatalf("Error getting test AP_REQ: %v", err)
}
ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false)
if ok || err == nil {
t.Fatal("Validation of AP_REQ passed when it should not have")
}
if _, ok := err.(messages.KRBError); ok {
assert.Equal(t, errorcode.KRB_AP_ERR_TKT_NYV, err.(messages.KRBError).ErrorCode, "Error code not as expected")
} else {
t.Fatalf("Error is not a KRBError: %v", err)
}
}
func TestValidateAPREQ_InvalidTicket(t *testing.T) {
t.Parallel()
cl := getClient()
sname := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
st := time.Now().UTC()
f := types.NewKrbFlags()
types.SetFlag(&f, flags.Invalid)
tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
f,
kt,
18,
1,
st,
st,
st.Add(time.Duration(24)*time.Hour),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
APReq, err := messages.NewAPReq(
tkt,
sessionKey,
newTestAuthenticator(*cl.Credentials),
)
if err != nil {
t.Fatalf("Error getting test AP_REQ: %v", err)
}
ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false)
if ok || err == nil {
t.Fatal("Validation of AP_REQ passed when it should not have")
}
if _, ok := err.(messages.KRBError); ok {
assert.Equal(t, errorcode.KRB_AP_ERR_TKT_NYV, err.(messages.KRBError).ErrorCode, "Error code not as expected")
} else {
t.Fatalf("Error is not a KRBError: %v", err)
}
}
func TestValidateAPREQ_ExpiredTicket(t *testing.T) {
t.Parallel()
cl := getClient()
sname := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
st := time.Now().UTC()
tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
types.NewKrbFlags(),
kt,
18,
1,
st,
st,
st.Add(time.Duration(-30)*time.Minute),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
a := newTestAuthenticator(*cl.Credentials)
APReq, err := messages.NewAPReq(
tkt,
sessionKey,
a,
)
if err != nil {
t.Fatalf("Error getting test AP_REQ: %v", err)
}
ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false)
if ok || err == nil {
t.Fatal("Validation of AP_REQ passed when it should not have")
}
if _, ok := err.(messages.KRBError); ok {
assert.Equal(t, errorcode.KRB_AP_ERR_TKT_EXPIRED, err.(messages.KRBError).ErrorCode, "Error code not as expected")
} else {
t.Fatalf("Error is not a KRBError: %v", err)
}
}
func newTestAuthenticator(creds credentials.Credentials) types.Authenticator {
auth, _ := types.NewAuthenticator(creds.Realm, creds.CName)
auth.GenerateSeqNumberAndSubKey(18, 32)
//auth.Cksum = types.Checksum{
// CksumType: chksumtype.GSSAPI,
// Checksum: newAuthenticatorChksum([]int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}),
//}
return auth
}
func getClient() client.Client {
b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
kt, _ := keytab.Parse(b)
c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
cl := client.NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
cl.WithConfig(c)
return cl
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/authenticator.go 0000664 0000000 0000000 00000012073 13625372257 0025421 0 ustar 00root root 0000000 0000000 package service
import (
"encoding/base64"
"errors"
"fmt"
"strings"
"time"
goidentity "gopkg.in/jcmturner/goidentity.v2"
"gopkg.in/jcmturner/gokrb5.v5/client"
"gopkg.in/jcmturner/gokrb5.v5/config"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/gssapi"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
)
// SPNEGOAuthenticator implements gopkg.in/jcmturner/goidentity.v2.Authenticator interface
type SPNEGOAuthenticator struct {
SPNEGOHeaderValue string
Keytab *keytab.Keytab
ServiceAccount string
ClientAddr string
RequireHostAddr bool
}
// Authenticate and retrieve a goidentity.Identity. In this case it is a pointer to a credentials.Credentials
func (a SPNEGOAuthenticator) Authenticate() (i goidentity.Identity, ok bool, err error) {
b, err := base64.StdEncoding.DecodeString(a.SPNEGOHeaderValue)
if err != nil {
err = fmt.Errorf("SPNEGO error in base64 decoding negotiation header: %v", err)
return
}
var spnego gssapi.SPNEGO
err = spnego.Unmarshal(b)
if !spnego.Init {
err = fmt.Errorf("SPNEGO negotiation token is not a NegTokenInit: %v", err)
return
}
if !spnego.NegTokenInit.MechTypes[0].Equal(gssapi.MechTypeOIDKRB5) {
err = errors.New("SPNEGO OID of MechToken is not of type KRB5")
return
}
var mt gssapi.MechToken
err = mt.Unmarshal(spnego.NegTokenInit.MechToken)
if err != nil {
err = fmt.Errorf("SPNEGO error unmarshaling MechToken: %v", err)
return
}
if !mt.IsAPReq() {
err = errors.New("MechToken does not contain an AP_REQ - KRB_AP_ERR_MSG_TYPE")
return
}
ok, c, err := ValidateAPREQ(mt.APReq, *a.Keytab, a.ServiceAccount, a.ClientAddr, a.RequireHostAddr)
if err != nil {
err = fmt.Errorf("SPNEGO validation error: %v", err)
return
}
i = &c
return
}
// Mechanism returns the authentication mechanism.
func (a SPNEGOAuthenticator) Mechanism() string {
return "SPNEGO Kerberos"
}
// KRB5BasicAuthenticator implements gopkg.in/jcmturner/goidentity.v2.Authenticator interface.
// It takes username and password so can be used for basic authentication.
type KRB5BasicAuthenticator struct {
BasicHeaderValue string
realm string
username string
password string
ServiceKeytab *keytab.Keytab
ServiceAccount string
Config *config.Config
SPN string
}
// Authenticate and return the identity. The boolean indicates if the authentication was successful.
func (a KRB5BasicAuthenticator) Authenticate() (i goidentity.Identity, ok bool, err error) {
a.realm, a.username, a.password, err = parseBasicHeaderValue(a.BasicHeaderValue)
if err != nil {
err = fmt.Errorf("could not parse basic authentication header: %v", err)
return
}
cl := client.NewClientWithPassword(a.username, a.realm, a.password)
cl.WithConfig(a.Config)
err = cl.Login()
if err != nil {
// Username and/or password could be wrong
err = fmt.Errorf("error with user credentials during login: %v", err)
return
}
tkt, _, err := cl.GetServiceTicket(a.SPN)
if err != nil {
err = fmt.Errorf("could not get service ticket: %v", err)
return
}
err = tkt.DecryptEncPart(*a.ServiceKeytab, a.ServiceAccount)
if err != nil {
err = fmt.Errorf("could not decrypt service ticket: %v", err)
return
}
cl.Credentials.SetAuthTime(time.Now().UTC())
cl.Credentials.SetAuthenticated(true)
isPAC, pac, err := tkt.GetPACType(*a.ServiceKeytab, a.ServiceAccount)
if isPAC && err != nil {
err = fmt.Errorf("error processing PAC: %v", err)
return
}
if isPAC {
// There is a valid PAC. Adding attributes to creds
cl.Credentials.SetADCredentials(credentials.ADCredentials{
GroupMembershipSIDs: pac.KerbValidationInfo.GetGroupMembershipSIDs(),
LogOnTime: pac.KerbValidationInfo.LogOnTime.Time(),
LogOffTime: pac.KerbValidationInfo.LogOffTime.Time(),
PasswordLastSet: pac.KerbValidationInfo.PasswordLastSet.Time(),
EffectiveName: pac.KerbValidationInfo.EffectiveName.Value,
FullName: pac.KerbValidationInfo.FullName.Value,
UserID: int(pac.KerbValidationInfo.UserID),
PrimaryGroupID: int(pac.KerbValidationInfo.PrimaryGroupID),
LogonServer: pac.KerbValidationInfo.LogonServer.Value,
LogonDomainName: pac.KerbValidationInfo.LogonDomainName.Value,
LogonDomainID: pac.KerbValidationInfo.LogonDomainID.ToString(),
})
}
ok = true
i = cl.Credentials
return
}
// Mechanism returns the authentication mechanism.
func (a KRB5BasicAuthenticator) Mechanism() string {
return "Kerberos Basic"
}
func parseBasicHeaderValue(s string) (domain, username, password string, err error) {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return
}
v := string(b)
vc := strings.SplitN(v, ":", 2)
password = vc[1]
// Domain and username can be specified in 2 formats:
// - no domain specified
// \
// @
if strings.Contains(vc[0], `\`) {
u := strings.SplitN(vc[0], `\`, 2)
domain = u[0]
username = u[1]
} else if strings.Contains(vc[0], `@`) {
u := strings.SplitN(vc[0], `@`, 2)
domain = u[1]
username = u[0]
} else {
username = vc[0]
}
return
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/authenticator_test.go 0000664 0000000 0000000 00000000602 13625372257 0026453 0 ustar 00root root 0000000 0000000 package service
import (
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/goidentity.v2"
)
func TestImplementsInterface(t *testing.T) {
t.Parallel()
//s := new(SPNEGOAuthenticator)
var s SPNEGOAuthenticator
a := new(goidentity.Authenticator)
assert.Implements(t, a, s, "SPNEGOAuthenticator type does not implement the goidentity.Authenticator interface")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/cache.go 0000664 0000000 0000000 00000006554 13625372257 0023621 0 ustar 00root root 0000000 0000000 // Package service provides server side integrations for Kerberos authentication.
package service
import (
"gopkg.in/jcmturner/gokrb5.v5/types"
"sync"
"time"
)
// Cache for tickets received from clients keyed by fully qualified client name. Used to track replay of tickets.
type Cache struct {
Entries map[string]clientEntries
mux sync.RWMutex
}
// clientEntries holds entries of client details sent to the service.
type clientEntries struct {
ReplayMap map[time.Time]replayCacheEntry
SeqNumber int64
SubKey types.EncryptionKey
}
// Cache entry tracking client time values of tickets sent to the service.
type replayCacheEntry struct {
PresentedTime time.Time
SName types.PrincipalName
CTime time.Time // This combines the ticket's CTime and Cusec
}
func (c *Cache) getClientEntries(cname types.PrincipalName) (clientEntries, bool) {
c.mux.RLock()
defer c.mux.RUnlock()
ce, ok := c.Entries[cname.GetPrincipalNameString()]
return ce, ok
}
func (c *Cache) getClientEntry(cname types.PrincipalName, t time.Time) (replayCacheEntry, bool) {
if ce, ok := c.getClientEntries(cname); ok {
c.mux.RLock()
defer c.mux.RUnlock()
if e, ok := ce.ReplayMap[t]; ok {
return e, true
}
}
return replayCacheEntry{}, false
}
// Instance of the ServiceCache. This needs to be a singleton.
var replayCache Cache
var once sync.Once
// GetReplayCache returns a pointer to the Cache singleton.
func GetReplayCache(d time.Duration) *Cache {
// Create a singleton of the ReplayCache and start a background thread to regularly clean out old entries
once.Do(func() {
replayCache = Cache{
Entries: make(map[string]clientEntries),
}
go func() {
for {
// TODO consider using a context here.
time.Sleep(d)
replayCache.ClearOldEntries(d)
}
}()
})
return &replayCache
}
// AddEntry adds an entry to the Cache.
func (c *Cache) AddEntry(sname types.PrincipalName, a types.Authenticator) {
ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond)
if ce, ok := c.getClientEntries(a.CName); ok {
c.mux.Lock()
defer c.mux.Unlock()
ce.ReplayMap[ct] = replayCacheEntry{
PresentedTime: time.Now().UTC(),
SName: sname,
CTime: ct,
}
ce.SeqNumber = a.SeqNumber
ce.SubKey = a.SubKey
} else {
c.mux.Lock()
defer c.mux.Unlock()
c.Entries[a.CName.GetPrincipalNameString()] = clientEntries{
ReplayMap: map[time.Time]replayCacheEntry{
ct: {
PresentedTime: time.Now().UTC(),
SName: sname,
CTime: ct,
},
},
SeqNumber: a.SeqNumber,
SubKey: a.SubKey,
}
}
}
// ClearOldEntries clears entries from the Cache that are older than the duration provided.
func (c *Cache) ClearOldEntries(d time.Duration) {
c.mux.Lock()
defer c.mux.Unlock()
for ke, ce := range c.Entries {
for k, e := range ce.ReplayMap {
if time.Now().UTC().Sub(e.PresentedTime) > d {
delete(ce.ReplayMap, k)
}
}
if len(ce.ReplayMap) == 0 {
delete(c.Entries, ke)
}
}
}
// IsReplay tests if the Authenticator provided is a replay within the duration defined. If this is not a replay add the entry to the cache for tracking.
func (c *Cache) IsReplay(sname types.PrincipalName, a types.Authenticator) bool {
ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond)
if e, ok := c.getClientEntry(a.CName, ct); ok {
if e.SName.Equal(sname) {
return true
}
}
c.AddEntry(sname, a)
return false
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/http.go 0000664 0000000 0000000 00000012527 13625372257 0023532 0 ustar 00root root 0000000 0000000 package service
import (
"context"
"encoding/base64"
"fmt"
"log"
"net/http"
"strings"
"gopkg.in/jcmturner/gokrb5.v5/gssapi"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
)
// POTENTIAL BREAKING CHANGE notice. Context keys used will change to a name-spaced strings to avoid clashes.
// If you are using the constants service.CTXKeyAuthenticated and service.CTXKeyCredentials
// defined below when retrieving data from the request context your code will be unaffected.
// However if, for example, you are retrieving context like this: r.Context().Value(1) then
// you will need to update to replace the 1 with service.CTXKeyCredentials.
type ctxKey int
const (
// spnegoNegTokenRespKRBAcceptCompleted - The response on successful authentication always has this header. Capturing as const so we don't have marshaling and encoding overhead.
spnegoNegTokenRespKRBAcceptCompleted = "Negotiate oRQwEqADCgEAoQsGCSqGSIb3EgECAg=="
// spnegoNegTokenRespReject - The response on a failed authentication always has this rejection header. Capturing as const so we don't have marshaling and encoding overhead.
spnegoNegTokenRespReject = "Negotiate oQcwBaADCgEC"
// CTXKeyAuthenticated is the request context key holding a boolean indicating if the request has been authenticated.
CTXKeyAuthenticated ctxKey = 0
// CTXKeyCredentials is the request context key holding the credentials gopkg.in/jcmturner/goidentity.v2/Identity object.
CTXKeyCredentials ctxKey = 1
// HTTPHeaderAuthRequest is the header that will hold authn/z information.
HTTPHeaderAuthRequest = "Authorization"
// HTTPHeaderAuthResponse is the header that will hold SPNEGO data from the server.
HTTPHeaderAuthResponse = "WWW-Authenticate"
// HTTPHeaderAuthResponseValueKey is the key in the auth header for SPNEGO.
HTTPHeaderAuthResponseValueKey = "Negotiate"
// UnauthorizedMsg is the message returned in the body when authentication fails.
UnauthorizedMsg = "Unauthorised.\n"
)
// SPNEGOKRB5Authenticate is a Kerberos SPNEGO authentication HTTP handler wrapper.
//
// kt - keytab for the service user
//
// ktprinc - keytab principal override for the service.
// The service looks for this principal in the keytab to use to decrypt tickets.
// If "" is passed as ktprinc then the principal will be automatically derived
// from the service name (SName) and realm in the ticket the service is trying to decrypt.
// This is often sufficient if you create the SPN in MIT KDC with: /usr/sbin/kadmin.local -q "add_principal HTTP/"
// When Active Directory is used for the KDC this may need to be the account name you have set the SPN against
// (setspn.exe -a "HTTP/" )
// If you are unsure run:
//
// klist -k
//
// and use the value from the Principal column for the keytab entry the service should use.
func SPNEGOKRB5Authenticate(f http.Handler, kt keytab.Keytab, ktprinc string, requireHostAddr bool, l *log.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s := strings.SplitN(r.Header.Get(HTTPHeaderAuthRequest), " ", 2)
if len(s) != 2 || s[0] != HTTPHeaderAuthResponseValueKey {
w.Header().Set(HTTPHeaderAuthResponse, HTTPHeaderAuthResponseValueKey)
w.WriteHeader(401)
w.Write([]byte(UnauthorizedMsg))
return
}
b, err := base64.StdEncoding.DecodeString(s[1])
if err != nil {
rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO error in base64 decoding negotiation header: %v", r.RemoteAddr, err))
return
}
var spnego gssapi.SPNEGO
err = spnego.Unmarshal(b)
if !spnego.Init {
rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO negotiation token is not a NegTokenInit: %v", r.RemoteAddr, err))
return
}
if !spnego.NegTokenInit.MechTypes[0].Equal(gssapi.MechTypeOIDKRB5) && !spnego.NegTokenInit.MechTypes[0].Equal(gssapi.MechTypeOIDMSLegacyKRB5) {
rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO OID of MechToken is not of type KRB5", r.RemoteAddr))
return
}
var mt gssapi.MechToken
err = mt.Unmarshal(spnego.NegTokenInit.MechToken)
if err != nil {
rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO error unmarshaling MechToken: %v", r.RemoteAddr, err))
return
}
if !mt.IsAPReq() {
rejectSPNEGO(w, l, fmt.Sprintf("%v - MechToken does not contain an AP_REQ - KRB_AP_ERR_MSG_TYPE", r.RemoteAddr))
return
}
if ok, creds, err := ValidateAPREQ(mt.APReq, kt, ktprinc, r.RemoteAddr, requireHostAddr); ok {
ctx := r.Context()
ctx = context.WithValue(ctx, CTXKeyCredentials, creds)
ctx = context.WithValue(ctx, CTXKeyAuthenticated, true)
if l != nil {
l.Printf("%v %s@%s - SPNEGO authentication succeeded", r.RemoteAddr, creds.Username, creds.Realm)
}
spnegoResponseAcceptCompleted(w)
f.ServeHTTP(w, r.WithContext(ctx))
} else {
rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO Kerberos authentication failed: %v", r.RemoteAddr, err))
return
}
})
}
// Set the headers for a rejected SPNEGO negotiation and return an unauthorized status code.
func rejectSPNEGO(w http.ResponseWriter, l *log.Logger, logMsg string) {
if l != nil {
l.Println(logMsg)
}
spnegoResponseReject(w)
}
func spnegoResponseReject(w http.ResponseWriter) {
w.Header().Set(HTTPHeaderAuthResponse, spnegoNegTokenRespReject)
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(UnauthorizedMsg))
}
func spnegoResponseAcceptCompleted(w http.ResponseWriter) {
w.Header().Set(HTTPHeaderAuthResponse, spnegoNegTokenRespKRBAcceptCompleted)
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/http_test.go 0000664 0000000 0000000 00000016441 13625372257 0024570 0 ustar 00root root 0000000 0000000 package service
import (
"encoding/hex"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/client"
"gopkg.in/jcmturner/gokrb5.v5/credentials"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/keytab"
"gopkg.in/jcmturner/gokrb5.v5/messages"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
"gopkg.in/jcmturner/gokrb5.v5/types"
)
func TestService_SPNEGOKRB_NoAuthHeader(t *testing.T) {
s := httpServer()
defer s.Close()
r, _ := http.NewRequest("GET", s.URL, nil)
httpResp, err := http.DefaultClient.Do(r)
if err != nil {
t.Fatalf("Request error: %v\n", err)
}
assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected")
assert.Equal(t, "Negotiate", httpResp.Header.Get("WWW-Authenticate"), "Negitation header not set by server.")
}
func TestService_SPNEGOKRB_ValidUser(t *testing.T) {
s := httpServer()
defer s.Close()
cl := getClient()
sname := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
st := time.Now().UTC()
tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
types.NewKrbFlags(),
kt,
18,
1,
st,
st,
st.Add(time.Duration(24)*time.Hour),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
r, _ := http.NewRequest("GET", s.URL, nil)
err = client.SetSPNEGOHeader(*cl.Credentials, tkt, sessionKey, r)
if err != nil {
t.Fatalf("Error setting client SPNEGO header: %v", err)
}
httpResp, err := http.DefaultClient.Do(r)
if err != nil {
t.Fatalf("Request error: %v\n", err)
}
assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected")
}
func TestService_SPNEGOKRB_Replay(t *testing.T) {
s := httpServer()
defer s.Close()
cl := getClient()
sname := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
st := time.Now().UTC()
tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
types.NewKrbFlags(),
kt,
18,
1,
st,
st,
st.Add(time.Duration(24)*time.Hour),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
r1, _ := http.NewRequest("GET", s.URL, nil)
err = client.SetSPNEGOHeader(*cl.Credentials, tkt, sessionKey, r1)
if err != nil {
t.Fatalf("Error setting client SPNEGO header: %v", err)
}
// First request with this ticket should be accepted
httpResp, err := http.DefaultClient.Do(r1)
if err != nil {
t.Fatalf("Request error: %v\n", err)
}
assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected")
// Use ticket again should be rejected
httpResp, err = http.DefaultClient.Do(r1)
if err != nil {
t.Fatalf("Request error: %v\n", err)
}
assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected. Expected a replay to be detected.")
// Form a 2nd ticket
st = time.Now().UTC()
tkt2, sessionKey2, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
types.NewKrbFlags(),
kt,
18,
1,
st,
st,
st.Add(time.Duration(24)*time.Hour),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
r2, _ := http.NewRequest("GET", s.URL, nil)
err = client.SetSPNEGOHeader(*cl.Credentials, tkt2, sessionKey2, r2)
if err != nil {
t.Fatalf("Error setting client SPNEGO header: %v", err)
}
// First use of 2nd ticket should be accepted
httpResp, err = http.DefaultClient.Do(r2)
if err != nil {
t.Fatalf("Request error: %v\n", err)
}
assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected")
// Using the 1st ticket again should still be rejected
httpResp, err = http.DefaultClient.Do(r1)
if err != nil {
t.Fatalf("Request error: %v\n", err)
}
assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected. Expected a replay to be detected.")
// Using the 2nd again should be rejected as replay
httpResp, err = http.DefaultClient.Do(r2)
if err != nil {
t.Fatalf("Request error: %v\n", err)
}
assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected. Expected a replay to be detected.")
}
func TestService_SPNEGOKRB_ReplayCache_Concurrency(t *testing.T) {
s := httpServer()
defer s.Close()
cl := getClient()
sname := types.PrincipalName{
NameType: nametype.KRB_NT_PRINCIPAL,
NameString: []string{"HTTP", "host.test.gokrb5"},
}
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
st := time.Now().UTC()
tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
types.NewKrbFlags(),
kt,
18,
1,
st,
st,
st.Add(time.Duration(24)*time.Hour),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
r1, _ := http.NewRequest("GET", s.URL, nil)
err = client.SetSPNEGOHeader(*cl.Credentials, tkt, sessionKey, r1)
if err != nil {
t.Fatalf("Error setting client SPNEGO header: %v", err)
}
// Form a 2nd ticket
st = time.Now().UTC()
tkt2, sessionKey2, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm,
sname, "TEST.GOKRB5",
types.NewKrbFlags(),
kt,
18,
1,
st,
st,
st.Add(time.Duration(24)*time.Hour),
st.Add(time.Duration(48)*time.Hour),
)
if err != nil {
t.Fatalf("Error getting test ticket: %v", err)
}
r2, _ := http.NewRequest("GET", s.URL, nil)
err = client.SetSPNEGOHeader(*cl.Credentials, tkt2, sessionKey2, r2)
if err != nil {
t.Fatalf("Error setting client SPNEGO header: %v", err)
}
// Concurrent 1st requests should be OK
var wg sync.WaitGroup
wg.Add(2)
go httpGet(r1, &wg)
go httpGet(r2, &wg)
wg.Wait()
// A number of concurrent requests with the same ticket should be rejected due to replay
var wg2 sync.WaitGroup
noReq := 10
wg2.Add(noReq * 2)
for i := 0; i < noReq; i++ {
go httpGet(r1, &wg2)
go httpGet(r2, &wg2)
}
wg2.Wait()
}
func httpGet(r *http.Request, wg *sync.WaitGroup) {
defer wg.Done()
http.DefaultClient.Do(r)
}
func httpServer() *httptest.Server {
l := log.New(ioutil.Discard, "GOKRB5 Service Tests: ", log.Ldate|log.Ltime|log.Lshortfile)
b, _ := hex.DecodeString(testdata.HTTP_KEYTAB)
kt, _ := keytab.Parse(b)
th := http.HandlerFunc(testAppHandler)
s := httptest.NewServer(SPNEGOKRB5Authenticate(th, kt, "", false, l))
return s
}
func testAppHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
ctx := r.Context()
fmt.Fprintf(w, "\nTEST.GOKRB5 Handler\nAuthenticed user: %s\nUser's realm: %s\n", ctx.Value(CTXKeyCredentials).(credentials.Credentials).Username, ctx.Value(CTXKeyCredentials).(credentials.Credentials).Realm)
return
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/testdata/ 0000775 0000000 0000000 00000000000 13625372257 0022366 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/testdata/test_vectors.go 0000664 0000000 0000000 00000175646 13625372257 0025464 0 ustar 00root root 0000000 0000000 // Package testdata provides Kerberos 5 test reference data.
package testdata
const (
//Expected unmarshaled values
TEST_REALM = "ATHENA.MIT.EDU"
TEST_CIPHERTEXT = "krbASN.1 test message"
TEST_TIME_FORMAT = "20060102150405"
TEST_TIME = "19940610060317"
TEST_ETYPE int32 = 0
TEST_NONCE = 42
TEST_AUTHORIZATION_DATA_VALUE = "foobar"
TEST_PADATA_VALUE = "pa-data"
)
var TEST_PRINCIPALNAME_NAMESTRING = []string{"hftsai", "extra"}
//The test vectors have been sourced from https://github.com/krb5/krb5/blob/master/src/tests/asn.1/reference_encode.out
var TestVectors = map[string]string{
"encode_krb5_authenticator": "6281A130819EA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A30F300DA003020101A106040431323334A405020301E240A511180F31393934303631303036303331375AA6133011A003020101A10A04083132333435363738A703020111A8243022300FA003020101A1080406666F6F626172300FA003020101A1080406666F6F626172",
"encode_krb5_authenticator(optionalsempty)": "624F304DA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A405020301E240A511180F31393934303631303036303331375A",
"encode_krb5_authenticator(optionalsNULL)": "624F304DA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A405020301E240A511180F31393934303631303036303331375A",
"encode_krb5_ticket": "615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_keyblock": "3011A003020101A10A04083132333435363738",
"encode_krb5_enc_tkt_part": "6382011430820110A007030500FEDCBA98A1133011A003020101A10A04083132333435363738A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A42E302CA003020101A12504234544552C4D49542E2C415448454E412E2C57415348494E47544F4E2E4544552C43532EA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA711180F31393934303631303036303331375AA811180F31393934303631303036303331375AA920301E300DA003020102A106040412D00023300DA003020102A106040412D00023AA243022300FA003020101A1080406666F6F626172300FA003020101A1080406666F6F626172",
"encode_krb5_enc_tkt_part(optionalsNULL)": "6381A53081A2A007030500FEDCBA98A1133011A003020101A10A04083132333435363738A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A42E302CA003020101A12504234544552C4D49542E2C415448454E412E2C57415348494E47544F4E2E4544552C43532EA511180F31393934303631303036303331375AA711180F31393934303631303036303331375A",
"encode_krb5_enc_kdc_rep_part": "7A82010E3082010AA0133011A003020101A10A04083132333435363738A13630343018A0030201FBA111180F31393934303631303036303331375A3018A0030201FBA111180F31393934303631303036303331375AA20302012AA311180F31393934303631303036303331375AA407030500FEDCBA98A511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA711180F31393934303631303036303331375AA811180F31393934303631303036303331375AA9101B0E415448454E412E4D49542E454455AA1A3018A003020101A111300F1B066866747361691B056578747261AB20301E300DA003020102A106040412D00023300DA003020102A106040412D00023",
"encode_krb5_enc_kdc_rep_part(optionalsNULL)": "7A81B23081AFA0133011A003020101A10A04083132333435363738A13630343018A0030201FBA111180F31393934303631303036303331375A3018A0030201FBA111180F31393934303631303036303331375AA20302012AA407030500FE5CBA98A511180F31393934303631303036303331375AA711180F31393934303631303036303331375AA9101B0E415448454E412E4D49542E454455AA1A3018A003020101A111300F1B066866747361691B056578747261",
"encode_krb5_as_rep": "6B81EA3081E7A003020105A10302010BA22630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A3101B0E415448454E412E4D49542E454455A41A3018A003020101A111300F1B066866747361691B056578747261A55E615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A6253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_as_rep(optionalsNULL)": "6B81C23081BFA003020105A10302010BA3101B0E415448454E412E4D49542E454455A41A3018A003020101A111300F1B066866747361691B056578747261A55E615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A6253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_tgs_rep": "6D81EA3081E7A003020105A10302010DA22630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A3101B0E415448454E412E4D49542E454455A41A3018A003020101A111300F1B066866747361691B056578747261A55E615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A6253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_tgs_rep(optionalsNULL)": "6D81C23081BFA003020105A10302010DA3101B0E415448454E412E4D49542E454455A41A3018A003020101A111300F1B066866747361691B056578747261A55E615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A6253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_ap_req": "6E819D30819AA003020105A10302010EA207030500FEDCBA98A35E615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A4253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_ap_rep": "6F333031A003020105A10302010FA2253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_ap_rep_enc_part": "7B363034A011180F31393934303631303036303331375AA105020301E240A2133011A003020101A10A04083132333435363738A303020111",
"encode_krb5_ap_rep_enc_part(optionalsNULL)": "7B1C301AA011180F31393934303631303036303331375AA105020301E240",
"encode_krb5_as_req": "6A8201E4308201E0A103020105A20302010AA32630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A48201AA308201A6A007030500FEDCBA90A11A3018A003020101A111300F1B066866747361691B056578747261A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA70302012AA8083006020100020101A920301E300DA003020102A106040412D00023300DA003020102A106040412D00023AA253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_as_req(optionalsNULLexceptsecond_ticket)": "6A82011430820110A103020105A20302010AA48201023081FFA007030500FEDCBA98A2101B0E415448454E412E4D49542E454455A511180F31393934303631303036303331375AA70302012AA8083006020100020101AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_as_req(optionalsNULLexceptserver)": "6A693067A103020105A20302010AA45B3059A007030500FEDCBA90A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A511180F31393934303631303036303331375AA70302012AA8083006020100020101",
"encode_krb5_tgs_req": "6C8201E4308201E0A103020105A20302010CA32630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A48201AA308201A6A007030500FEDCBA90A11A3018A003020101A111300F1B066866747361691B056578747261A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA70302012AA8083006020100020101A920301E300DA003020102A106040412D00023300DA003020102A106040412D00023AA253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_tgs_req(optionalsNULLexceptsecond_ticket)": "6C82011430820110A103020105A20302010CA48201023081FFA007030500FEDCBA98A2101B0E415448454E412E4D49542E454455A511180F31393934303631303036303331375AA70302012AA8083006020100020101AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_tgs_req(optionalsNULLexceptserver)": "6C693067A103020105A20302010CA45B3059A007030500FEDCBA90A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A511180F31393934303631303036303331375AA70302012AA8083006020100020101",
"encode_krb5_kdc_req_body": "308201A6A007030500FEDCBA90A11A3018A003020101A111300F1B066866747361691B056578747261A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA70302012AA8083006020100020101A920301E300DA003020102A106040412D00023300DA003020102A106040412D00023AA253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_kdc_req_body(optionalsNULLexceptsecond_ticket)": "3081FFA007030500FEDCBA98A2101B0E415448454E412E4D49542E454455A511180F31393934303631303036303331375AA70302012AA8083006020100020101AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_kdc_req_body(optionalsNULLexceptserver)": "3059A007030500FEDCBA90A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A511180F31393934303631303036303331375AA70302012AA8083006020100020101",
"encode_krb5_safe": "746E306CA003020105A103020114A24F304DA00A04086B72623564617461A111180F31393934303631303036303331375AA205020301E240A303020111A40F300DA003020102A106040412D00023A50F300DA003020102A106040412D00023A30F300DA003020101A106040431323334",
"encode_krb5_safe(optionalsNULL)": "743E303CA003020105A103020114A21F301DA00A04086B72623564617461A40F300DA003020102A106040412D00023A30F300DA003020101A106040431323334",
"encode_krb5_priv": "75333031A003020105A103020115A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_enc_priv_part": "7C4F304DA00A04086B72623564617461A111180F31393934303631303036303331375AA205020301E240A303020111A40F300DA003020102A106040412D00023A50F300DA003020102A106040412D00023",
"encode_krb5_enc_priv_part(optionalsNULL)": "7C1F301DA00A04086B72623564617461A40F300DA003020102A106040412D00023",
"encode_krb5_cred": "7681F63081F3A003020105A103020116A281BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_enc_cred_part": "7D8202233082021FA08201DA308201D63081E8A0133011A003020101A10A04083132333435363738A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A307030500FEDCBA98A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA711180F31393934303631303036303331375AA8101B0E415448454E412E4D49542E454455A91A3018A003020101A111300F1B066866747361691B056578747261AA20301E300DA003020102A106040412D00023300DA003020102A106040412D000233081E8A0133011A003020101A10A04083132333435363738A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A307030500FEDCBA98A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA711180F31393934303631303036303331375AA8101B0E415448454E412E4D49542E454455A91A3018A003020101A111300F1B066866747361691B056578747261AA20301E300DA003020102A106040412D00023300DA003020102A106040412D00023A10302012AA211180F31393934303631303036303331375AA305020301E240A40F300DA003020102A106040412D00023A50F300DA003020102A106040412D00023",
"encode_krb5_enc_cred_part(optionalsNULL)": "7D82010E3082010AA0820106308201023015A0133011A003020101A10A040831323334353637383081E8A0133011A003020101A10A04083132333435363738A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A307030500FEDCBA98A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA711180F31393934303631303036303331375AA8101B0E415448454E412E4D49542E454455A91A3018A003020101A111300F1B066866747361691B056578747261AA20301E300DA003020102A106040412D00023300DA003020102A106040412D00023",
"encode_krb5_error": "7E81BA3081B7A003020105A10302011EA211180F31393934303631303036303331375AA305020301E240A411180F31393934303631303036303331375AA505020301E240A60302013CA7101B0E415448454E412E4D49542E454455A81A3018A003020101A111300F1B066866747361691B056578747261A9101B0E415448454E412E4D49542E454455AA1A3018A003020101A111300F1B066866747361691B056578747261AB0A1B086B72623564617461AC0A04086B72623564617461",
"encode_krb5_error(optionalsNULL)": "7E60305EA003020105A10302011EA305020301E240A411180F31393934303631303036303331375AA505020301E240A60302013CA9101B0E415448454E412E4D49542E454455AA1A3018A003020101A111300F1B066866747361691B056578747261",
"encode_krb5_authorization_data": "3022300FA003020101A1080406666F6F626172300FA003020101A1080406666F6F626172",
"encode_krb5_padata_sequence": "30243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461",
"encode_krb5_typed_data": "30243010A00302010DA109040770612D646174613010A00302010DA109040770612D64617461",
"encode_krb5_padata_sequence(empty)": "3000",
"encode_krb5_etype_info": "30333014A003020100A10D040B4D6F72746F6E27732023303005A0030201013014A003020102A10D040B4D6F72746F6E2773202332",
"encode_krb5_etype_info(only1)": "30163014A003020100A10D040B4D6F72746F6E2773202330",
"encode_krb5_etype_info(noinfo)": "3000",
"encode_krb5_etype_info2": "3051301EA003020100A10D1B0B4D6F72746F6E2773202330A208040673326B3A2030300FA003020101A208040673326B3A2031301EA003020102A10D1B0B4D6F72746F6E2773202332A208040673326B3A2032",
"encode_krb5_etype_info2(only1)": "3020301EA003020100A10D1B0B4D6F72746F6E2773202330A208040673326B3A2030",
"encode_krb5_pa_enc_ts": "301AA011180F31393934303631303036303331375AA105020301E240",
"encode_krb5_pa_enc_ts(nousec)": "3013A011180F31393934303631303036303331375A",
"encode_krb5_enc_data": "3023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_enc_data(MSB-setkvno)": "3026A003020100A1060204FF000000A21704156B726241534E2E312074657374206D657373616765",
"encode_krb5_enc_data(kvno= -1)": "3023A003020100A1030201FFA21704156B726241534E2E312074657374206D657373616765",
//"encode_krb5_sam_challenge_2": "3022A00D300B04096368616C6C656E6765A111300F300DA003020101A106040431323334",
//"encode_krb5_sam_challenge_2_body": "3064A00302012AA10703050080000000A20B040974797065206E616D65A411040F6368616C6C656E6765206C6162656CA510040E6368616C6C656E67652069707365A6160414726573706F6E73655F70726F6D70742069707365A8050203543210A903020101",
//"encode_krb5_sam_response_2": "3042A00302012BA10703050080000000A20C040A747261636B2064617461A31D301BA003020101A10402020D36A20E040C6E6F6E6365206F7220736164A4050203543210",
//"encode_krb5_enc_sam_response_enc_2": "301FA003020158A1180416656E635F73616D5F726573706F6E73655F656E635F32",
//"encode_krb5_pa_for_user": "304BA01A3018A003020101A111300F1B066866747361691B056578747261A1101B0E415448454E412E4D49542E454455A20F300DA003020101A106040431323334A30A1B086B72623564617461",
//"encode_krb5_pa_s4u_x509_user": "3068A0553053A006020400CA149AA11A3018A003020101A111300F1B066866747361691B056578747261A2101B0E415448454E412E4D49542E454455A312041070615F7334755F783530395F75736572A40703050080000000A10F300DA003020101A106040431323334",
"encode_krb5_ad_kdcissued": "3065A00F300DA003020101A106040431323334A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3243022300FA003020101A1080406666F6F626172300FA003020101A1080406666F6F626172",
//"encode_krb5_ad_signedpath_data": "3081C7A030302EA01A3018A003020101A111300F1B066866747361691B056578747261A1101B0E415448454E412E4D49542E454455A111180F31393934303631303036303331375AA2323030302EA01A3018A003020101A111300F1B066866747361691B056578747261A1101B0E415448454E412E4D49542E454455A32630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A4243022300FA003020101A1080406666F6F626172300FA003020101A1080406666F6F626172",
//"encode_krb5_ad_signedpath": "303EA003020101A10F300DA003020101A106040431323334A32630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461",
//"encode_krb5_iakerb_header": "3018A10A04086B72623564617461A20A04086B72623564617461",
//"encode_krb5_iakerb_finished": "3011A10F300DA003020101A106040431323334",
//"encode_krb5_fast_response": "30819FA02630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A1133011A003020101A10A04083132333435363738A25B3059A011180F31393934303631303036303331375AA105020301E240A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A40F300DA003020101A106040431323334A30302012A",
//"encode_krb5_pa_fx_fast_reply": "A0293027A0253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
//"encode_krb5_otp_tokeninfo(optionalsNULL)": "300780050000000000",
//"encode_krb5_otp_tokeninfo": "307280050077000000810B4578616D706C65636F727082056861726B2183010A8401028509796F7572746F6B656E862875726E3A696574663A706172616D733A786D6C3A6E733A6B657970726F763A70736B633A686F7470A716300B0609608648016503040201300706052B0E03021A880203E8",
//"encode_krb5_pa_otp_challenge(optionalsNULL)": "301580086D696E6E6F6E6365A209300780050000000000",
//"encode_krb5_pa_otp_challenge": "3081A580086D61786E6F6E6365810B7465737473657276696365A27D300780050000000000307280050077000000810B4578616D706C65636F727082056861726B2183010A8401028509796F7572746F6B656E862875726E3A696574663A706172616D733A786D6C3A6E733A6B657970726F763A70736B633A686F7470A716300B0609608648016503040201300706052B0E03021A880203E883076B657973616C74840431323334",
//"encode_krb5_pa_otp_req(optionalsNULL)": "302C80050000000000A223A003020100A103020105A21704156B726241534E2E312074657374206D657373616765",
//"encode_krb5_pa_otp_req": "3081B98005006000000081056E6F6E6365A223A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A30B0609608648016503040201840203E8850566726F6773860A6D79666972737470696E87056861726B21880F31393934303631303036303331375A89033334368A01028B09796F7572746F6B656E8C2875726E3A696574663A706172616D733A786D6C3A6E733A6B657970726F763A70736B633A686F74708D0B4578616D706C65636F7270",
//"encode_krb5_pa_otp_enc_req": "300A80086B72623564617461",
//"encode_krb5_kkdcp_message": "308201FCA08201EC048201E86A8201E4308201E0A103020105A20302010AA32630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A48201AA308201A6A007030500FEDCBA98A11A3018A003020101A111300F1B066866747361691B056578747261A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA70302012AA8083006020100020101A920301E300DA003020102A106040412D00023300DA003020102A106040412D00023AA253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A10A1B086B72623564617461",
//"encode_krb5_cammac(optionalsNULL)": "3012A010300E300CA003020101A1050403616431",
//"encode_krb5_cammac": "3081F2A01E301C300CA003020101A1050403616431300CA003020102A1050403616432A13D303BA01A3018A003020101A111300F1B066866747361691B056578747261A103020105A203020110A3133011A003020101A10A0408636B73756D6B6463A23D303BA01A3018A003020101A111300F1B066866747361691B056578747261A103020105A203020110A3133011A003020101A10A0408636B73756D737663A35230503013A311300FA003020101A1080406636B73756D313039A01A3018A003020101A111300F1B066866747361691B056578747261A103020105A203020110A311300FA003020101A1080406636B73756D32",
//"encode_krb5_secure_cookie": "302C02042DF8022530243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461",
//MS PAC Vectors
"PAC_AuthorizationData_MS": "308205523082054ea00402020080a182054404820540040000000000000001000000b004000048000000000000000a00000012000000f804000000000000060000001400000010050000000000000700000014000000280500000000000001100800cccccccca00400000000000000000200d186660f656ac601ffffffffffffff7fffffffffffffff7f17d439fe784ac6011794a328424bc601175424977a81c60108000800040002002400240008000200120012000c0002000000000010000200000000001400020000000000180002005410000097792c00010200001a0000001c000200200000000000000000000000000000000000000016001800200002000a000c002400020028000200000000000000000010000000000000000000000000000000000000000000000000000000000000000d0000002c0002000000000000000000000000000400000000000000040000006c007a00680075001200000000000000120000004c0069007100690061006e00670028004c006100720072007900290020005a00680075000900000000000000090000006e0074006400730032002e0062006100740000000000000000000000000000000000000000000000000000000000000000000000000000001a00000061c433000700000009c32d00070000005eb4320007000000010200000700000097b92c00070000002bf1320007000000ce30330007000000a72e2e00070000002af132000700000098b92c000700000062c4330007000000940133000700000076c4330007000000aefe2d000700000032d22c00070000001608320007000000425b2e00070000005fb4320007000000ca9c35000700000085442d0007000000c2f0320007000000e9ea310007000000ed8e2e0007000000b6eb310007000000ab2e2e0007000000720e2e00070000000c000000000000000b0000004e0054004400450056002d00440043002d003000350000000600000000000000050000004e0054004400450056000000040000000104000000000005150000005951b81766725d2564633b0b0d0000003000020007000000340002000700002038000200070000203c000200070000204000020007000020440002000700002048000200070000204c000200070000205000020007000020540002000700002058000200070000205c00020007000020600002000700002005000000010500000000000515000000b9301b2eb7414c6c8c3b351501020000050000000105000000000005150000005951b81766725d2564633b0b74542f00050000000105000000000005150000005951b81766725d2564633b0be8383200050000000105000000000005150000005951b81766725d2564633b0bcd383200050000000105000000000005150000005951b81766725d2564633b0b5db43200050000000105000000000005150000005951b81766725d2564633b0b41163500050000000105000000000005150000005951b81766725d2564633b0be8ea3100050000000105000000000005150000005951b81766725d2564633b0bc1193200050000000105000000000005150000005951b81766725d2564633b0b29f13200050000000105000000000005150000005951b81766725d2564633b0b0f5f2e00050000000105000000000005150000005951b81766725d2564633b0b2f5b2e00050000000105000000000005150000005951b81766725d2564633b0bef8f3100050000000105000000000005150000005951b81766725d2564633b0b075f2e00000000000049d90e656ac60108006c007a006800750000000000000076ffffff41edce9a34815d3aef7bc98874805d250000000076fffffff7a534dab2c02986efe0fbe5110a4f3200000000",
"PAC_Kerb_Validation_Info_MS": "01100800cccccccca00400000000000000000200d186660f656ac601ffffffffffffff7fffffffffffffff7f17d439fe784ac6011794a328424bc601175424977a81c60108000800040002002400240008000200120012000c0002000000000010000200000000001400020000000000180002005410000097792c00010200001a0000001c000200200000000000000000000000000000000000000016001800200002000a000c002400020028000200000000000000000010000000000000000000000000000000000000000000000000000000000000000d0000002c0002000000000000000000000000000400000000000000040000006c007a00680075001200000000000000120000004c0069007100690061006e00670028004c006100720072007900290020005a00680075000900000000000000090000006e0074006400730032002e0062006100740000000000000000000000000000000000000000000000000000000000000000000000000000001a00000061c433000700000009c32d00070000005eb4320007000000010200000700000097b92c00070000002bf1320007000000ce30330007000000a72e2e00070000002af132000700000098b92c000700000062c4330007000000940133000700000076c4330007000000aefe2d000700000032d22c00070000001608320007000000425b2e00070000005fb4320007000000ca9c35000700000085442d0007000000c2f0320007000000e9ea310007000000ed8e2e0007000000b6eb310007000000ab2e2e0007000000720e2e00070000000c000000000000000b0000004e0054004400450056002d00440043002d003000350000000600000000000000050000004e0054004400450056000000040000000104000000000005150000005951b81766725d2564633b0b0d0000003000020007000000340002000700002038000200070000203c000200070000204000020007000020440002000700002048000200070000204c000200070000205000020007000020540002000700002058000200070000205c00020007000020600002000700002005000000010500000000000515000000b9301b2eb7414c6c8c3b351501020000050000000105000000000005150000005951b81766725d2564633b0b74542f00050000000105000000000005150000005951b81766725d2564633b0be8383200050000000105000000000005150000005951b81766725d2564633b0bcd383200050000000105000000000005150000005951b81766725d2564633b0b5db43200050000000105000000000005150000005951b81766725d2564633b0b41163500050000000105000000000005150000005951b81766725d2564633b0be8ea3100050000000105000000000005150000005951b81766725d2564633b0bc1193200050000000105000000000005150000005951b81766725d2564633b0b29f13200050000000105000000000005150000005951b81766725d2564633b0b0f5f2e00050000000105000000000005150000005951b81766725d2564633b0b2f5b2e00050000000105000000000005150000005951b81766725d2564633b0bef8f3100050000000105000000000005150000005951b81766725d2564633b0b075f2e0000000000",
"PAC_AuthorizationData_GOKRB5": "3082034730820343a003020101a182033a04820336308203323082032ea00402020080a1820324048203200500000000000000010000002802000058000000000000000a0000001c00000080020000000000000c00000058000000a0020000000000000600000010000000f8020000000000000700000014000000080300000000000001100800cccccccc180200000000000000000200058e4fdd80c6d201ffffffffffffff7fffffffffffffff7fcc27969c39c6d201cce7ffc602c7d201ffffffffffffff7f12001200040002001600160008000200000000000c000200000000001000020000000000140002000000000018000200d80000005104000001020000050000001c000200200000000000000000000000000000000000000008000a002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000020000002c00020000000000000000000000000009000000000000000900000074006500730074007500730065007200310000000b000000000000000b000000540065007300740031002000550073006500720031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000102000007000000540400000700000055040000070000005b040000070000005c0400000700000005000000000000000400000041004400440043000500000000000000040000005400450053005400040000000104000000000005150000004c86cebca07160e63fdce8870200000030000200070000203400020007000020050000000105000000000005150000004c86cebca07160e63fdce8875a040000050000000105000000000005150000004c86cebca07160e63fdce8875704000000000000808dd1dc80c6d2011200740065007300740075007300650072003100000000002a001000160040000000000000000000740065007300740075007300650072003100400074006500730074002e0067006f006b0072006200350000000000000054004500530054002e0047004f004b005200420035000000100000001e251d98d552be7df384f55076ffffff340be28b48765d0519ee9346cf53d82200000000",
"PAC_AD_WIN2K_PAC": "0500000000000000010000002802000058000000000000000a0000001c00000080020000000000000c00000058000000a0020000000000000600000010000000f8020000000000000700000014000000080300000000000001100800cccccccc180200000000000000000200058e4fdd80c6d201ffffffffffffff7fffffffffffffff7fcc27969c39c6d201cce7ffc602c7d201ffffffffffffff7f12001200040002001600160008000200000000000c000200000000001000020000000000140002000000000018000200d80000005104000001020000050000001c000200200000000000000000000000000000000000000008000a002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000020000002c00020000000000000000000000000009000000000000000900000074006500730074007500730065007200310000000b000000000000000b000000540065007300740031002000550073006500720031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000102000007000000540400000700000055040000070000005b040000070000005c0400000700000005000000000000000400000041004400440043000500000000000000040000005400450053005400040000000104000000000005150000004c86cebca07160e63fdce8870200000030000200070000203400020007000020050000000105000000000005150000004c86cebca07160e63fdce8875a040000050000000105000000000005150000004c86cebca07160e63fdce8875704000000000000808dd1dc80c6d2011200740065007300740075007300650072003100000000002a001000160040000000000000000000740065007300740075007300650072003100400074006500730074002e0067006f006b0072006200350000000000000054004500530054002e0047004f004b005200420035000000100000001e251d98d552be7df384f55076ffffff340be28b48765d0519ee9346cf53d82200000000",
"PAC_Kerb_Validation_Info": "01100800cccccccc180200000000000000000200058e4fdd80c6d201ffffffffffffff7fffffffffffffff7fcc27969c39c6d201cce7ffc602c7d201ffffffffffffff7f12001200040002001600160008000200000000000c000200000000001000020000000000140002000000000018000200d80000005104000001020000050000001c000200200000000000000000000000000000000000000008000a002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000020000002c00020000000000000000000000000009000000000000000900000074006500730074007500730065007200310000000b000000000000000b000000540065007300740031002000550073006500720031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000102000007000000540400000700000055040000070000005b040000070000005c0400000700000005000000000000000400000041004400440043000500000000000000040000005400450053005400040000000104000000000005150000004c86cebca07160e63fdce8870200000030000200070000203400020007000020050000000105000000000005150000004c86cebca07160e63fdce8875a040000050000000105000000000005150000004c86cebca07160e63fdce8875704000000000000",
"PAC_Client_Info": "808dd1dc80c6d2011200740065007300740075007300650072003100",
"PAC_UPN_DNS_Info": "2a001000160040000000000000000000740065007300740075007300650072003100400074006500730074002e0067006f006b0072006200350000000000000054004500530054002e0047004f004b005200420035000000",
"PAC_Server_Signature": "100000001e251d98d552be7df384f550",
"PAC_KDC_Signature": "76ffffff340be28b48765d0519ee9346cf53d822",
"PAC_Kerb_Validation_Info_Trust": "01100800cccccccc000200000000000000000200c30bcc79e444d301ffffffffffffff7fffffffffffffff7fc764125a0842d301c7247c84d142d301ffffffffffffff7f12001200040002001600160008000200000000000c0002000000000010000200000000001400020000000000180002002e0000005204000001020000030000001c0002002002000000000000000000000000000000000000060008002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000010000002c00020034000200020000003800020009000000000000000900000074006500730074007500730065007200310000000b000000000000000b0000005400650073007400310020005500730065007200310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000056040000070000000102000007000000550400000700000004000000000000000300000055004400430000000500000000000000040000005500530045005200040000000104000000000005150000002057308834e7d1d0a2fb0444010000003000020007000000010000000101000000000012010000000400000001040000000000051500000062dc8db6c8705249b5459e75020000005304000007000020540400000700002000000000",
"PAC_ClientClaimsInfoStr": "01100800cccccccc000100000000000000000200d80000000400020000000000d8000000000000000000000000000000d800000001100800ccccccccc80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200030003000100000010000200290000000000000029000000610064003a002f002f006500780074002f00730041004d004100630063006f0075006e0074004e0061006d0065003a0038003800640035006400390030003800350065006100350063003000630030000000000001000000140002000a000000000000000a00000074006500730074007500730065007200310000000000000000000000",
"PAC_ClientClaimsInfoInt": "01100800cccccccce00000000000000000000200b80000000400020000000000b8000000000000000000000000000000b800000001100800cccccccca80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c0002000100010001000000100002002a000000000000002a000000610064003a002f002f006500780074002f006d007300440053002d0053007500700070006f00720074006500640045003a0038003800640035006400650061003800660031006100660035006600310039000000010000001c0000000000000000000000",
"PAC_ClientClaimsInfoMulti": "01100800cccccccc780100000000000000000200500100000400020000000000500100000000000000000000000000005001000001100800cccccccc400100000000000000000200010000000400020000000000000000000000000001000000010000000200000008000200020000000c000200010001000100000010000200140002000300030001000000180002002a000000000000002a000000610064003a002f002f006500780074002f006d007300440053002d0053007500700070006f00720074006500640045003a0038003800640035006400650061003800660031006100660035006600310039000000010000001c00000000000000290000000000000029000000610064003a002f002f006500780074002f00730041004d004100630063006f0075006e0074004e0061006d0065003a00380038006400350064003900300038003500650061003500630030006300300000000000010000001c0002000a000000000000000a000000740065007300740075007300650072003100000000000000",
"PAC_ClientClaimsInfoMultiUint": "01100800ccccccccf00000000000000000000200c80000000400020000000000c8000000000000000000000000000000c800000001100800ccccccccb80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200020002000400000010000200260000000000000026000000610064003a002f002f006500780074002f006f0062006a0065006300740043006c006100730073003a00380038006400350064006500370039003100650037006200320037006500360000000400000009000a000000000007000100000000000600010000000000000001000000000000000000",
"PAC_ClientClaimsInfoMultiStr": "01100800cccccccc480100000000000000000200200100000400020000000000200100000000000000000000000000002001000001100800cccccccc100100000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200030003000400000010000200270000000000000027000000610064003a002f002f006500780074002f006f00740068006500720049007000500068006f006e0065003a003800380064003500640065003900660036006200340061006600390038003500000000000400000014000200180002001c000200200002000500000000000000050000007300740072003100000000000500000000000000050000007300740072003200000000000500000000000000050000007300740072003300000000000500000000000000050000007300740072003400000000000000000000000000",
"PAC_ClientClaimsInfo_XPRESS_HUFF": "01100800ccccccccd00100000000000000000200a80100000400020004000000e0010000000000000000000000000000a8010000727807888708080007000800080008000800080880000080870870887807000080800000000080080000080000000000605767070007777707677700770000000000000000000000000000000000000000000000000000000000000000000000000000000000070007000000000000000000000000000000000000000000000076000700700000007600000000000000750700000000000064770700000000007607000000000000060700000000000077060700000000707770700070000770007700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a85652950bb9d8bae030b2212b90df95764d1b182da22f2c848b23b3cc4efc8e3499701e481cf938e490986a384c3d572250aaab2446572fc26be279c263e4a4c9c2c24f9649e2444d8ddb3277373c600363beb73200baaa783da183dd85830af863e1a00d5cf718aac4879519fbf0745bcc59214493a330f940bf99a446f1ade6df2610c5f154b432eaba964d7ad1f1182e522019fc21ce498a204d06b96a476f7386e6003000000000000",
"ChangePasswdData": "3036a00d040b6e657770617373776f7264a1163014a003020101a10d300b1b09746573747573657231a20d1b0b544553542e474f4b524235",
"Kpasswd_Req": "037aff8002c16e8202bd308202b9a003020105a10302010ea20703050000000000a38201f1618201ed308201e9a003020105a10d1b0b544553542e474f4b524235a21d301ba003020101a11430121b066b61646d696e1b086368616e67657077a38201b2308201aea003020111a103020101a28201a00482019ca3de94df50e8e9fe7a8c9386f594f469bf08874407fc7b95ddcf22110ef63e62ff0ba3c31c3bb725dc1dde1f4c2f69a4973b4b43c9b4b31f71f676d5e8e7b4d7906b1dfacc9897d865b17f934fb96b802344463bb0746fdd39e9e48ff1b2665dc895a74d3d3aac89512b43bd8ead8f455b9b819cc6f6a34fb7c5975d7c2dbd4349524961215b98f33f5747f1e0c89f3b3637462308953940741ab7fc38ae817ba85800dd911bb78b42264f2d285c2a0a33ca21c1a3d281ec14614010db31c3e3f4d4622b799f97b3d31c4445411278fec62dd8e6e349db280aaa4419b53ef6fbc01f0206bcfea2cbe835b46764c03c138722e54dab53a1080e5d6c99f8cd7a948880677176cfc2d3800f9ef64d1ec4f8bdadc1ae409990c4855a82e265682e8ddaa6dea70a1d7855f3e1e766f5efe428dd6da71c585f5d17d8f81e8f2a4f4b2245f5ff2cc444a2a1ae5d16a15d588597219d5659da537f752ca9b572b635088b325b60e8e62fd99487872261f41dcc466516b89992d277bb8b3a1ca770671fca36dd33c3dd6dab643e6710280661029254054273151ccfca9aaddc55a481ae3081aba003020112a103020101a2819e04819be66387f971d751d7d3ebb6acd815a0991e0ed9f07e2643783e7961fb88127b31f767bf00d1d071a81858b101f4d45460412d8013228f942bc51891e95a06aefa8cedd95e5a3e6e65597c0f05c19ee54dc6dc00b1a3f9d7a95516b5e447c40cd5b462ed6b17a007670311efa44dbe939cab11072b9af1443c3203767bb1a3240542db06dffcaebcedd5c335bb295127bc0e6d99f2c1e87f68de1f547581b03081ada003020105a103020115a381a030819da003020112a103020101a2819004818df272b2726c8f31c578f3b4275bc283828716010a20f0c4369bff474fcf202537060a71edcbe8ba720d0d9b2bac26b58353dc5b2945570374928a819eb3526362eda328e704f1a5ebe3272eed0fa6a6aa7d0f32c4fc0bd2e4ea52a8834ea7b5fb018934df87c18ab625f5c07f6c28e202e0cec63bcc37b1d381d64937998c1bdcd1585695eeffb75f8ce9e736b3",
"Kpasswd_Rep": "00ec0001008c6f8189308186a003020105a10302010fa27a3078a003020112a271046f57cb442fd321312aff0b2dcda70fe436812f9805611adf3403ab6cd7708604e86e77f765a8486864f0dbf8d5d065a63790370bc110ed1e3c7eae9890e02407e8a8b349703fed1e7f165e1261a822c5b3e6823c282884f59afeb9f84f2a9845994135dd307eb2f544874393c1c455d475583056a003020105a103020115a34a3048a003020112a241043fdd3edaf0b6cbcab5b663189bafc0a19e6cc03b3c59d989c403735748ebc36088bad852add0f62581eed515fc1f297324df4fa12cb94b7ad5db257165369db5",
}
const (
TESTUSER1_PASSWORD = "passwordvalue"
TESTUSER1_KEYTAB = "05020000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80100110010698c4df8e9f60e7eea5a21bf4526ad25000000010000004b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80100120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9000000010000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80200110010698c4df8e9f60e7eea5a21bf4526ad25000000020000004b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80200120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9000000020000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d801001300102eb8501967a7886e1f0c63ac9be8c4a0000000010000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d802001300102eb8501967a7886e1f0c63ac9be8c4a0000000020000004b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d801001400208ad66f209bb07daa186f8a229830f5ba06a3a2a33638f4ec66e1d29324e417ee000000010000004b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d802001400208ad66f209bb07daa186f8a229830f5ba06a3a2a33638f4ec66e1d29324e417ee00000002000000430001000b544553542e474f4b52423500097465737475736572310000000159beb1d801001000184580fb91760dabe6f808c22c26494f644cb35d61d32c79e300000001000000430001000b544553542e474f4b52423500097465737475736572310000000159beb1d802001000184580fb91760dabe6f808c22c26494f644cb35d61d32c79e3000000020000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80100170010084768c373663b3bef1f6385883cf7ff000000010000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80200170010084768c373663b3bef1f6385883cf7ff00000002"
TESTUSER1_USERKRB5_AD_KEYTAB = "05020000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040100170010084768c373663b3bef1f6385883cf7ff000000010000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040100110010528b8ba0ae5131fbf71f6ddc5870cdce000000010000004b0001000b555345522e474f4b52423500097465737475736572310000000159de7c04010012002016475f89eba70e62af20a20e7bf3ca4ccad5ae22485c93ffb133650bc6f12585000000010000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040100130010a3ddea306fa06c068bc3e1fcf4b280ca000000010000004b0001000b555345522e474f4b52423500097465737475736572310000000159de7c0401001400205d2a66a8af5142db59bcaabac8310777bf60a85e8881469e2063bba4dff0c00a000000010000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040200170010084768c373663b3bef1f6385883cf7ff000000020000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040200110010528b8ba0ae5131fbf71f6ddc5870cdce000000020000004b0001000b555345522e474f4b52423500097465737475736572310000000159de7c04020012002016475f89eba70e62af20a20e7bf3ca4ccad5ae22485c93ffb133650bc6f12585000000020000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040200130010a3ddea306fa06c068bc3e1fcf4b280ca000000020000004b0001000b555345522e474f4b52423500097465737475736572310000000159de7c0402001400205d2a66a8af5142db59bcaabac8310777bf60a85e8881469e2063bba4dff0c00a00000002"
TESTUSER2_USERKRB5_AD_KEYTAB = "05020000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d0100170010084768c373663b3bef1f6385883cf7ff000000010000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d0100110010a771a31fae504621fffc644a521e0cee000000010000004b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d01001200203262b201af7ec73c77bbee75a4ff10950cf2bda56529ead30ced4f0f0b9a591d000000010000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d01001300104be17a4cf1761f0494475617f671fa6a000000010000004b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d01001400208b1c60589b6c0d78d5e8fe265e92c2babf920a6a2828c37fc343e43497ad9fab000000010000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d0200170010084768c373663b3bef1f6385883cf7ff000000020000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d0200110010a771a31fae504621fffc644a521e0cee000000020000004b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d02001200203262b201af7ec73c77bbee75a4ff10950cf2bda56529ead30ced4f0f0b9a591d000000020000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d02001300104be17a4cf1761f0494475617f671fa6a000000020000004b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d02001400208b1c60589b6c0d78d5e8fe265e92c2babf920a6a2828c37fc343e43497ad9fab00000002"
TESTUSER1_WRONGPASSWD = "0502000000370001000b544553542e474f4b52423500097465737475736572310000000158ef4bc5010011001039a9a382153105f8708e80f93382654e000000470001000b544553542e474f4b52423500097465737475736572310000000158ef4bc60100120020fc5bb940d6075214e0c6fc0456ce68c33306094198a927b4187d7cf3f4aea50d"
TESTUSER2_KEYTAB = "05020000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb240010011001086824c55ff5de30386dd83dc62b44bb7000000010000004b0001000b544553542e474f4b52423500097465737475736572320000000159beb2400100120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92000000010000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb240020011001086824c55ff5de30386dd83dc62b44bb7000000020000004b0001000b544553542e474f4b52423500097465737475736572320000000159beb2400200120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92000000020000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb24001001300106ccff358aaa8a4a41c444e173b1463c2000000010000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb24002001300106ccff358aaa8a4a41c444e173b1463c2000000020000004b0001000b544553542e474f4b52423500097465737475736572320000000159beb24001001400205cf3773dd920be800229ac1c6f9bf59c6706c583f82c2dea66c9a29152118cd7000000010000004b0001000b544553542e474f4b52423500097465737475736572320000000159beb24002001400205cf3773dd920be800229ac1c6f9bf59c6706c583f82c2dea66c9a29152118cd700000002000000430001000b544553542e474f4b52423500097465737475736572320000000159beb2400100100018bc025746e9e66bd6b62a918f6413d529803192a28aabf79200000001000000430001000b544553542e474f4b52423500097465737475736572320000000159beb2400200100018bc025746e9e66bd6b62a918f6413d529803192a28aabf792000000020000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb2400100170010084768c373663b3bef1f6385883cf7ff000000010000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb2400200170010084768c373663b3bef1f6385883cf7ff00000002"
TESTUSER3_KEYTAB = "05020000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740100110010220789f5cf68e44a852c73b1e3729efc000000010000004b0001000b544553542e474f4b52423500097465737475736572330000000159beb27401001200205e1b7287f78f3f88c9eefe92c08ec1613f35d72bc6ee2c2e1c5cb6fb9d8b0580000000010000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740200110010220789f5cf68e44a852c73b1e3729efc000000020000004b0001000b544553542e474f4b52423500097465737475736572330000000159beb27402001200205e1b7287f78f3f88c9eefe92c08ec1613f35d72bc6ee2c2e1c5cb6fb9d8b0580000000020000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740100130010d2509e2bf9c08f73aafe08745c85e8fd000000010000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740200130010d2509e2bf9c08f73aafe08745c85e8fd000000020000004b0001000b544553542e474f4b52423500097465737475736572330000000159beb274010014002060e5e3fb9166db825eb3e4ff4bd1f4307e3f5fe85cdf7faccf8d65330557f74e000000010000004b0001000b544553542e474f4b52423500097465737475736572330000000159beb274020014002060e5e3fb9166db825eb3e4ff4bd1f4307e3f5fe85cdf7faccf8d65330557f74e00000002000000430001000b544553542e474f4b52423500097465737475736572330000000159beb27401001000182cad768989a125fd26f24adf671976456d8097548c4c83f100000001000000430001000b544553542e474f4b52423500097465737475736572330000000159beb27402001000182cad768989a125fd26f24adf671976456d8097548c4c83f1000000020000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740100170010084768c373663b3bef1f6385883cf7ff000000010000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740200170010084768c373663b3bef1f6385883cf7ff00000002"
HTTP_KEYTAB = "0502000000440002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc010011001057a7754c70c4d85c155c718c2f1292b0000000540002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc01001200209cad00bbc72d703258e911dc18e6d5487cf737bf67fd111f0c2463ad6033bf51000000440002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc020011001057a7754c70c4d85c155c718c2f1292b0000000540002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc02001200209cad00bbc72d703258e911dc18e6d5487cf737bf67fd111f0c2463ad6033bf51"
SYSHTTP_KEYTAB = "0502000000450001000b544553542e474f4b52423500077379734854545000000001590dc5af020012002043763702868978d1b6d91a36704b987e27e517250055bdfc40b8a6b3848d9aae"
SYSHTTP_RESDOM_KEYTAB = "05020000005c0002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d94070100120020e53945463c231ab747635c96d5fc48f6591ce41cec98ad2620b50f52c2bafa96000000010000004c0002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d940701001100103ae5388332dc948e00427332658c537800000001000000540002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d940701001000183da7b93eb698233de3b07f080b07191a49a83d32d3587c8f000000010000004c0002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d9407010013001036e58aaaf739aad8bc49115dfd8d304b000000010000005c0002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d9407010014002055e4bf018dfdd3906f0f84149b6d503a03bdf494ba40f482faf67e7a77b9c05f000000010000004c0002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d94070100170010c050d33acce5fac748f6f26bd686e1c700000001"
SYSHTTP_RESGOKRB5_AD_KEYTAB = "0502000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b0100170010c050d33acce5fac748f6f26bd686e1c700000001000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b01001100100bed4565fa65bbcc167ee344775339c200000001000000480001000a5245532e474f4b5242350007737973485454500000000159de7b4b01001200209a5faf803b231d69ee7d559be62980cc01b9d4c67d18e42450920b0625a4dd2600000001000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b01001300103e8f9a29e92595691c9f753312ba4c7e00000001000000480001000a5245532e474f4b5242350007737973485454500000000159de7b4b0100140020bcf7aa970b530504cc610eefa4893b5e03a71f9f6962d993a36cf9fc7ba4d7bc00000001000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b0200170010c050d33acce5fac748f6f26bd686e1c700000002000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b02001100100bed4565fa65bbcc167ee344775339c200000002000000480001000a5245532e474f4b5242350007737973485454500000000159de7b4b02001200209a5faf803b231d69ee7d559be62980cc01b9d4c67d18e42450920b0625a4dd2600000002000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b02001300103e8f9a29e92595691c9f753312ba4c7e00000002000000480001000a5245532e474f4b5242350007737973485454500000000159de7b4b0200140020bcf7aa970b530504cc610eefa4893b5e03a71f9f6962d993a36cf9fc7ba4d7bc00000002"
TEST_AS_REQ = "6a81a63081a3a103020105a20302010aa30e300c300aa10402020095a2020400a48186308183a00703050040000010a1163014a003020101a10d300b1b09746573747573657231a20d1b0b544553542e474f4b524235a320301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a511180f32303137303232303134323530315aa70602040f6755a6a814301202011202011102011002011702011902011a"
TEST_AS_REP = "6b8202f3308202efa003020105a10302010ba22e302c302aa103020113a2230421301f301da003020112a1161b14544553542e474f4b524235746573747573657231a30d1b0b544553542e474f4b524235a4163014a003020101a10d300b1b09746573747573657231a582015a6182015630820152a003020105a10d1b0b544553542e474f4b524235a220301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a382011830820114a003020112a103020101a28201060482010264d3fa49d89b627ed471298846ff92cd8632f657c58fe25322a61fffa32bb7966dc4c44c86a81353def2a11c36c537191406a609147f424a63266c00d02bcc56a27b0969d86ff4352634be9e2a4ac0ad5a36b0b0a3d689f128c0afa97401796e88037a35ad19efaf31d1ed4f3213769c03a58bc90ffac2051db152c0ed0809ad05ffb03aa3afaf731ed85f7a73020cb72355e0de27842dcf7eae3de9f7c14aa237edb25153b217ef3693373bc3cacbebe406910ff9ae9d00b7b08f726cb29a213cb9ad51ba80a8c24fa4b6692a445686889702cfa6ea749bac03e27e982407aca623fbd48586bcf566cfe87e1d9f17a74b1315669c16480f93e9d8782e71a8f11000a682012c30820128a003020112a282011f0482011b99b86153c0393c0e4130628f3e1e0f0a1f034e7e61a111b7fad15884e231c8fd8727e0bc945c9b35be20c57d057c8b09b0de74c53fb38cc15c9a2d483023fc369f5bde4da7324b4732b5a3d9504d92f67026aaa01df4f0138245d2ccb1c5a4014804cf295c7e7e56a867e6cf0c534f667f32da7aa5e700af1461764f1c276a8ff0fbee0e99322fe2059d2321853be09d0956c3afcfd07e3e702646a4678926a77bea20d9aaf3086b6d384821c81900af9013a3519f0e50eab6e1491d72e4ee17c2a44441b2ebc8a796cc3d876e328347dce65f61104e14d4c31532885776c9c8a70186b8b39f928972945c98bd60381ead5448e7ebe93fea308054287ac34b0583b4b9b5e43c5f8518d693ba9eb48a219c27344466b3c693a70462"
TEST_TGS_REQ = "6c82038f3082038ba103020105a20302010ca382031a3082031630820245a103020101a282023c048202386e82023430820230a003020105a10302010ea20703050000000000a382015a6182015630820152a003020105a10d1b0b544553542e474f4b524235a220301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a382011830820114a003020112a103020101a28201060482010264d3fa49d89b627ed471298846ff92cd8632f657c58fe25322a61fffa32bb7966dc4c44c86a81353def2a11c36c537191406a609147f424a63266c00d02bcc56a27b0969d86ff4352634be9e2a4ac0ad5a36b0b0a3d689f128c0afa97401796e88037a35ad19efaf31d1ed4f3213769c03a58bc90ffac2051db152c0ed0809ad05ffb03aa3afaf731ed85f7a73020cb72355e0de27842dcf7eae3de9f7c14aa237edb25153b217ef3693373bc3cacbebe406910ff9ae9d00b7b08f726cb29a213cb9ad51ba80a8c24fa4b6692a445686889702cfa6ea749bac03e27e982407aca623fbd48586bcf566cfe87e1d9f17a74b1315669c16480f93e9d8782e71a8f11000a481bc3081b9a003020112a281b10481ae8ae3cb8ac47d77cfc7b0b6bf0d3c5f8fcc6dd569344256a6a40c004fc2d23ebbe6ee0b9e00eccf37e710b7c01a7d2a63bbed6d75f2b230d24d724ef90edad2c5680e7e2436ab1145ff68481673444ebd61e3aef79b9ee05809551672c6c436eb8ac732a7fe78bd8f380e68a541191e3125554e4bab63dcc19ea931c1477366a6039ff7b7e62521ebfeffd6784b6ef0c97f653ac4d8dfb304f3e2e843faab12d838c23f1105f0a281c39325987cb03081caa10402020088a281c10481bea081bb3081b8a1173015a003020110a10e040ce613d8e9d544f0e56c60d3bba2819c308199a003020112a2819104818ec4fabcb1ec2f24e04ef51f9247239b28275653fa5cbc1dc9e747530c597631050fe86a5f3cba2ff54270aa771dcefa87efc8c8604407f84e603f5c01a2d929e18103561c3ffbc3a0cf63340bdd67a0739d4d81989827fc1d3f7f13e9dd5cc2346ca08e26a2aaf6d0102fbef8f7a6ee0a1caae7880e953ea678da619038786122a0b71853e8d0b95f544f8fbd6945a461305fa00703050040810000a20d1b0b544553542e474f4b524235a3233021a003020101a11a30181b04485454501b10686f73742e746573742e676f6b726235a511180f32303137303232303032323634325aa706020458a9ab2aa8053003020112"
TEST_TGS_REP = "6d82039d30820399a003020105a10302010da281df3081dc3081d9a10402020088a281d00481cda081ca3081c7a081c43081c1a003020112a281b90481b62e2b31bd998e2747a447175bcdbe14b7bc043c747c3289e10cde7403ee685d64f9724055c667ad2d3ce6240b5457ddfe3505a5b4d5d5e35238522ba5d86f1b691f28bbe7290487ffebf7e720ecf772cef061ac2c433cabbd0d5973fbedeb5418c8ba1bb7149dd625a4ff4cb465d9599c9d4cfb899807fa088521fa0e0ed5cfa4dd6d02b2a6854c4feb4ec382de5820134ca7c140be4b0f416ca5ebb328c8a470a55154062b8070683d0897a40277bdc752e94437920aa30d1b0b544553542e474f4b524235a4163014a003020101a10d300b1b09746573747573657231a58201706182016c30820168a003020105a10d1b0b544553542e474f4b524235a2233021a003020101a11a30181b04485454501b10686f73742e746573742e676f6b726235a382012b30820127a003020112a103020102a28201190482011524db012d81dde2cb3b7b40a35ce4fe17f7898166c7a7534ad73ca761e75316a06504415b1bc62508edb6ea58544a3ac0f98911cd55332442d30e007e0f39efa939b726a5228514984a133d1ba7d93d60fec5b2f7fbb16356c85ad1b0bb7ed6108420514086ea9959037e794b535fa052651385060da83f93acffbde0cb486b2f236f8955bb521ef90bc0a52944d2a8da82389e6861065064cc5178a5a7d302e9950761648726fc4015f8772339ef401fd8327dd335c6692c010f2c31b5ccccbcd83e68f2a1b66f16ca44e0bfb2903801c27ed8dd9a7b2dcea3c2bc91d8055c603f218494b1342490fbb805492d999491c2570b3ab392ba7e62c23659663509838e91b6f560a284889075071c348c701017a0a5e5c7a682010e3082010aa003020112a28201010481fece77ad9ebec9b094b686d4885895a2530471e9a56a93c2a463eaa988932695d77a12b6c02e69edfea6759ed49b029bdef43e446129b80a1ef95b9cea69a382d5c074868e68b676869f0990cad588c6d3589b9bba795ff0572ba648915f68a3b49df1def285645e93f4285a843ead01122c4f00bebefa33ecf6df2584e33da359d3d53e0a9b8ec0f41120a0d612d3a1e4504ce464dddec5e2aa3f0d2b6dae7b8800091760d7a590eaa6d7b33539c1700d9daf22a7c8cc1e108aee75267434997666132f52c08ea7c0c898859214ac4e71e185d3bcec1467ab6e8d91e79ba2b2041d40714da495c79d232d3c4e72a0910b9143fd648fe24bc46ec3416a3c34"
CCACHE_TEST = "0504000c00010008000000060000000000000001000000010000000b544553542e474f4b5242350000000974657374757365723100000001000000010000000b544553542e474f4b5242350000000974657374757365723100000002000000020000000b544553542e474f4b524235000000066b72627467740000000b544553542e474f4b52423500120000002088b94319f2dcd1de20ebd3bf3174778769323bce76ef71fb37a8ba4be93c38df59665b8e59665b8e5967044e5967ad080040c1000000000000000000000000015a6182015630820152a003020105a10d1b0b544553542e474f4b524235a220301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a382011830820114a003020112a103020101a282010604820102ee32bb7e27ad6f71869be098c4002b291f370d26302c87ffa3eb670345a11fc113a9e5ab9e26ea659104b29e2a60c07dda559654c58aaf5f48bbb3bb9a238745861be336a0672554dac9b38126b2929ce9df2add185d1043c6dd89c7308b9def7b98ba7bcdcd1c00eeb5d99e273e1fe53b88c057106ec3dbcf2a86c38a4c1372418f1afb0227975747edf2172e23716ab5f6fa9a2ee5c0d94e9f66936df767498677861926812d1f887de6f44e5ebd93b63fd8313a499372ea9e889620bd0842bc8a8f8a17e5dea328c77b771cfcd49ac7afa4a9c7236efa30fec1b2072255543aee48cd935ece367e08d24f51bea4b407ace8ed7e67a8d5e1cb528eb16c7ebe7ac50000000000000001000000010000000b544553542e474f4b5242350000000974657374757365723100000000000000030000000c582d4341434845434f4e463a000000156b7262355f6363616368655f636f6e665f646174610000000a666173745f617661696c0000001e6b72627467742f544553542e474f4b52423540544553542e474f4b5242350000000000000000000000000000000000000000000000000000000000000000000000000000037965730000000000000001000000010000000b544553542e474f4b5242350000000974657374757365723100000001000000020000000b544553542e474f4b524235000000044854545000000010686f73742e746573742e676f6b726235001200000020fd325da3f905d743894e828de41b21af7876b6281b66d9e4bb2eefd64078b47659665b8e59665bce5967044e5967ad0800408900000000000000000000000001706182016c30820168a003020105a10d1b0b544553542e474f4b524235a2233021a003020101a11a30181b04485454501b10686f73742e746573742e676f6b726235a382012b30820127a003020112a103020101a282011904820115ad55d79858ce41647e835769b40540bc32ff4debe101217a7a024016697ee5ff758829940ca576905a260732c43c2996d96b83f9bff010fdbfc8f3bff51cef202a956f8d73d18c2c8865553f55229075270f42dca23d7618ff35e578a972d40746398efd478cf4f1094d99371273b3fbe5b95707011b446ff605ea8cb0e6631ea0ffdd7b562b5aa2de5dd455388e1aa18d8a3a8e81dab058e1b223410a752e5ec82797164dabafdbec8eeef7b072304e46d7d15b575f44cce69a368a9004612ba179b41d4655964933f7eb114a457aa1127291fc6d63deb271e5504de6fccca33260645ef5bd1ea301d74a8dbf751aa181ed92f5edb493d68222e1a34892035b88b6fb0ce104db23f7da22a8e73359d9c322b8e1cc00000000"
TEST_HTTP_URL = "http://10.80.88.88/index.html"
TEST_KDC_ADDR = "10.80.88.88"
TEST_KDC = "88"
TEST_KDC_LASTEST = "98"
TEST_KDC_RESDOM = "188"
TEST_KDC_OLD = "78"
TEST_KDC_SHORTTICKETS = "58"
TEST_KDC_BADADDR = "10.80.88.153"
TEST_KDC_AD = "10.80.88.68:88"
TEST_KDC_AD_TRUST_USER_DOMAIN = "10.80.88.48:88"
TEST_KDC_AD_TRUST_RES_DOMAIN = "10.80.88.49:88"
TEST_NS = "10.80.88.88:53"
TEST_KRB5CONF = `[libdefaults]
default_realm = TEST.GOKRB5
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
forwardable = yes
default_tkt_enctypes = aes256-cts-hmac-sha1-96
default_tgs_enctypes = aes256-cts-hmac-sha1-96
noaddresses = false
[realms]
TEST.GOKRB5 = {
kdc = 127.0.0.1:88
admin_server = 127.0.0.1:749
default_domain = test.gokrb5
}
RESDOM.GOKRB5 = {
kdc = 10.80.88.88:188
admin_server = 127.0.0.1:749
default_domain = resdom.gokrb5
}
USER.GOKRB5 = {
kdc = 10.80.88.48:88
admin_server = 10.80.88.48:464
default_domain = user.gokrb5
}
RES.GOKRB5 = {
kdc = 10.80.88.49:88
admin_server = 10.80.88.49:464
default_domain = res.gokrb5
}
[domain_realm]
.test.gokrb5 = TEST.GOKRB5
test.gokrb5 = TEST.GOKRB5
.resdom.gokrb5 = RESDOM.GOKRB5
resdom.gokrb5 = RESDOM.GOKRB5
.user.gokrb5 = USER.GOKRB5
user.gokrb5 = USER.GOKRB5
.res.gokrb5 = RES.GOKRB5
res.gokrb5 = RES.GOKRB5
`
)
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/ 0000775 0000000 0000000 00000000000 13625372257 0021721 5 ustar 00root root 0000000 0000000 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/Authenticator.go 0000664 0000000 0000000 00000004777 13625372257 0025101 0 ustar 00root root 0000000 0000000 // Package types provides Kerberos 5 data types.
package types
import (
"crypto/rand"
"fmt"
"math"
"math/big"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/asn1tools"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag"
)
// Authenticator - A record containing information that can be shown to have been recently generated using the session key known only by the client and server.
// https://tools.ietf.org/html/rfc4120#section-5.5.1
type Authenticator struct {
AVNO int `asn1:"explicit,tag:0"`
CRealm string `asn1:"generalstring,explicit,tag:1"`
CName PrincipalName `asn1:"explicit,tag:2"`
Cksum Checksum `asn1:"explicit,optional,tag:3"`
Cusec int `asn1:"explicit,tag:4"`
CTime time.Time `asn1:"generalized,explicit,tag:5"`
SubKey EncryptionKey `asn1:"explicit,optional,tag:6"`
SeqNumber int64 `asn1:"explicit,optional,tag:7"`
AuthorizationData AuthorizationData `asn1:"explicit,optional,tag:8"`
}
// NewAuthenticator creates a new Authenticator.
func NewAuthenticator(realm string, cname PrincipalName) (Authenticator, error) {
seq, err := rand.Int(rand.Reader, big.NewInt(math.MaxUint32))
if err != nil {
return Authenticator{}, err
}
t := time.Now().UTC()
return Authenticator{
AVNO: iana.PVNO,
CRealm: realm,
CName: cname,
Cksum: Checksum{},
Cusec: int((t.UnixNano() / int64(time.Microsecond)) - (t.Unix() * 1e6)),
CTime: t,
SeqNumber: seq.Int64(),
}, nil
}
// GenerateSeqNumberAndSubKey sets the Authenticator's sequence number and subkey.
func (a *Authenticator) GenerateSeqNumberAndSubKey(keyType int32, keySize int) error {
seq, err := rand.Int(rand.Reader, big.NewInt(math.MaxUint32))
if err != nil {
return err
}
a.SeqNumber = seq.Int64()
//Generate subkey value
sk := make([]byte, keySize, keySize)
rand.Read(sk)
a.SubKey = EncryptionKey{
KeyType: keyType,
KeyValue: sk,
}
return nil
}
// Unmarshal bytes into the Authenticator.
func (a *Authenticator) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, a, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.Authenticator))
return err
}
// Marshal the Authenticator.
func (a *Authenticator) Marshal() ([]byte, error) {
b, err := asn1.Marshal(*a)
if err != nil {
return nil, err
}
b = asn1tools.AddASNAppTag(b, asnAppTag.Authenticator)
return b, nil
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/Authenticator_test.go 0000664 0000000 0000000 00000011031 13625372257 0026115 0 ustar 00root root 0000000 0000000 package types
import (
"encoding/hex"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/iana/adtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func unmarshalAuthenticatorTest(t *testing.T, v string) Authenticator {
var a Authenticator
//t.Logf("Starting unmarshal tests of %s", v)
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
return a
}
func TestUnmarshalAuthenticator(t *testing.T) {
t.Parallel()
a := unmarshalAuthenticatorTest(t, "encode_krb5_authenticator")
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.AVNO, "Authenticator version number not as expected")
assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected")
assert.Equal(t, int32(1), a.Cksum.CksumType, "Checksum type not as expected")
assert.Equal(t, []byte("1234"), a.Cksum.Checksum, "Checsum not as expected")
assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected")
assert.Equal(t, tt, a.CTime, "Client time not as expected")
assert.Equal(t, int32(1), a.SubKey.KeyType, "Subkey type not as expected")
assert.Equal(t, []byte("12345678"), a.SubKey.KeyValue, "Subkey value not as expected")
assert.Equal(t, 2, len(a.AuthorizationData), "Number of Authorization data items not as expected")
for i, entry := range a.AuthorizationData {
assert.Equal(t, adtype.ADIfRelevant, entry.ADType, fmt.Sprintf("Authorization type of entry %d not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_AUTHORIZATION_DATA_VALUE), entry.ADData, fmt.Sprintf("Authorization data of entry %d not as expected", i+1))
}
}
func TestUnmarshalAuthenticator_optionalsempty(t *testing.T) {
t.Parallel()
a := unmarshalAuthenticatorTest(t, "encode_krb5_authenticator(optionalsempty)")
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.AVNO, "Authenticator version number not as expected")
assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected")
assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected")
assert.Equal(t, tt, a.CTime, "Client time not as expected")
}
func TestUnmarshalAuthenticator_optionalsNULL(t *testing.T) {
t.Parallel()
a := unmarshalAuthenticatorTest(t, "encode_krb5_authenticator(optionalsNULL)")
//Parse the test time value into a time.Time type
tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
assert.Equal(t, iana.PVNO, a.AVNO, "Authenticator version number not as expected")
assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected")
assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected")
assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected")
assert.Equal(t, tt, a.CTime, "Client time not as expected")
}
func TestMarshalAuthenticator(t *testing.T) {
t.Parallel()
var a Authenticator
v := "encode_krb5_authenticator"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
mb, err := a.Marshal()
if err != nil {
t.Fatalf("Marshal of ticket errored: %v", err)
}
assert.Equal(t, b, mb, "Marshal bytes of Authenticator not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/AuthorizationData.go 0000664 0000000 0000000 00000003443 13625372257 0025706 0 ustar 00root root 0000000 0000000 package types
import (
"github.com/jcmturner/gofork/encoding/asn1"
)
// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.2.6
// AuthorizationData implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6
type AuthorizationData []AuthorizationDataEntry
// AuthorizationDataEntry implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6
type AuthorizationDataEntry struct {
ADType int32 `asn1:"explicit,tag:0"`
ADData []byte `asn1:"explicit,tag:1"`
}
// ADIfRelevant implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6.1
type ADIfRelevant AuthorizationData
// ADKDCIssued implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6.2
type ADKDCIssued struct {
ADChecksum Checksum `asn1:"explicit,tag:0"`
IRealm string `asn1:"optional,generalstring,explicit,tag:1"`
Isname PrincipalName `asn1:"optional,explicit,tag:2"`
Elements AuthorizationData `asn1:"explicit,tag:3"`
}
// ADAndOr implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6.3
type ADAndOr struct {
ConditionCount int32 `asn1:"explicit,tag:0"`
Elements AuthorizationData `asn1:"explicit,tag:1"`
}
// ADMandatoryForKDC implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6.4
type ADMandatoryForKDC AuthorizationData
// Unmarshal bytes into the ADKDCIssued.
func (a *ADKDCIssued) Unmarshal(b []byte) error {
_, err := asn1.Unmarshal(b, a)
return err
}
// Unmarshal bytes into the AuthorizationData.
func (a *AuthorizationData) Unmarshal(b []byte) error {
_, err := asn1.Unmarshal(b, a)
return err
}
// Unmarshal bytes into the AuthorizationDataEntry.
func (a *AuthorizationDataEntry) Unmarshal(b []byte) error {
_, err := asn1.Unmarshal(b, a)
return err
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/AuthorizationData_test.go 0000664 0000000 0000000 00000004177 13625372257 0026752 0 ustar 00root root 0000000 0000000 package types
import (
"encoding/hex"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana/adtype"
"gopkg.in/jcmturner/gokrb5.v5/iana/nametype"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestUnmarshalAuthorizationData(t *testing.T) {
t.Parallel()
var a AuthorizationData
v := "encode_krb5_authorization_data"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, 2, len(a), "Number of authorization data entries not as expected")
for i, entry := range a {
assert.Equal(t, adtype.ADIfRelevant, entry.ADType, fmt.Sprintf("Authorization data type of entry %d not as expected", i+1))
assert.Equal(t, []byte("foobar"), entry.ADData, fmt.Sprintf("Authorization data of entry %d not as expected", i+1))
}
}
func TestUnmarshalAuthorizationData_kdcissued(t *testing.T) {
t.Parallel()
var a ADKDCIssued
v := "encode_krb5_ad_kdcissued"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, int32(1), a.ADChecksum.CksumType, "Checksum type not as expected")
assert.Equal(t, []byte("1234"), a.ADChecksum.Checksum, "Checksum not as expected")
assert.Equal(t, testdata.TEST_REALM, a.IRealm, "Issuing realm not as expected")
assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Isname.NameType, "Issuing name type not as expected")
assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Isname.NameString, "Issuing name string entries not as expected")
assert.Equal(t, 2, len(a.Elements), "Number of authorization data elements not as expected")
for i, ele := range a.Elements {
assert.Equal(t, adtype.ADIfRelevant, ele.ADType, fmt.Sprintf("Authorization data type of element %d not as expected", i+1))
assert.Equal(t, []byte(testdata.TEST_AUTHORIZATION_DATA_VALUE), ele.ADData, fmt.Sprintf("Authorization data of element %d not as expected", i+1))
}
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/Cryptosystem.go 0000664 0000000 0000000 00000002565 13625372257 0025005 0 ustar 00root root 0000000 0000000 package types
import (
"github.com/jcmturner/gofork/encoding/asn1"
)
// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.2.9
// EncryptedData implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.9
type EncryptedData struct {
EType int32 `asn1:"explicit,tag:0"`
KVNO int `asn1:"explicit,optional,tag:1"`
Cipher []byte `asn1:"explicit,tag:2"`
}
// EncryptionKey implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.9
// AKA KeyBlock
type EncryptionKey struct {
KeyType int32 `asn1:"explicit,tag:0"`
KeyValue []byte `asn1:"explicit,tag:1"`
}
// Checksum implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.9
type Checksum struct {
CksumType int32 `asn1:"explicit,tag:0"`
Checksum []byte `asn1:"explicit,tag:1"`
}
// Unmarshal bytes into the EncryptedData.
func (a *EncryptedData) Unmarshal(b []byte) error {
_, err := asn1.Unmarshal(b, a)
return err
}
// Marshal the EncryptedData.
func (a *EncryptedData) Marshal() ([]byte, error) {
edb, err := asn1.Marshal(*a)
if err != nil {
return edb, err
}
return edb, nil
}
// Unmarshal bytes into the EncryptionKey.
func (a *EncryptionKey) Unmarshal(b []byte) error {
_, err := asn1.Unmarshal(b, a)
return err
}
// Unmarshal bytes into the Checksum.
func (a *Checksum) Unmarshal(b []byte) error {
_, err := asn1.Unmarshal(b, a)
return err
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/Cryptosystem_test.go 0000664 0000000 0000000 00000005673 13625372257 0026047 0 ustar 00root root 0000000 0000000 package types
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/jcmturner/gokrb5.v5/iana"
"gopkg.in/jcmturner/gokrb5.v5/testdata"
)
func TestUnmarshalEncryptedData(t *testing.T) {
t.Parallel()
var a EncryptedData
v := "encode_krb5_enc_data"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, testdata.TEST_ETYPE, a.EType, "Encrypted data Etype not as expected")
assert.Equal(t, iana.PVNO, a.KVNO, "Encrypted data KVNO not as expected")
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.Cipher, "Ecrypted data ciphertext not as expected")
}
func TestUnmarshalEncryptedData_MSBsetkvno(t *testing.T) {
t.Parallel()
var a EncryptedData
v := "encode_krb5_enc_data(MSB-setkvno)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, testdata.TEST_ETYPE, a.EType, "Encrypted data Etype not as expected")
assert.Equal(t, -16777216, a.KVNO, "Encrypted data KVNO not as expected")
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.Cipher, "Ecrypted data ciphertext not as expected")
}
func TestUnmarshalEncryptedData_kvno_neg1(t *testing.T) {
t.Parallel()
var a EncryptedData
v := "encode_krb5_enc_data(kvno= -1)"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, testdata.TEST_ETYPE, a.EType, "Encrypted data Etype not as expected")
assert.Equal(t, -1, a.KVNO, "Encrypted data KVNO not as expected")
assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.Cipher, "Ecrypted data ciphertext not as expected")
}
func TestUnmarshalEncryptionKey(t *testing.T) {
t.Parallel()
var a EncryptionKey
v := "encode_krb5_keyblock"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
assert.Equal(t, int32(1), a.KeyType, "Key type not as expected")
assert.Equal(t, []byte("12345678"), a.KeyValue, "Key value not as expected")
}
func TestMarshalEncryptedData(t *testing.T) {
t.Parallel()
var a EncryptedData
v := "encode_krb5_enc_data"
b, err := hex.DecodeString(testdata.TestVectors[v])
if err != nil {
t.Fatalf("Test vector read error of %s: %v\n", v, err)
}
err = a.Unmarshal(b)
if err != nil {
t.Fatalf("Unmarshal error of %s: %v\n", v, err)
}
mb, err := a.Marshal()
if err != nil {
t.Fatalf("Marshal of ticket errored: %v", err)
}
assert.Equal(t, b, mb, "Marshal bytes of Encrypted Data not as expected")
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/HostAddress.go 0000664 0000000 0000000 00000010065 13625372257 0024475 0 ustar 00root root 0000000 0000000 package types
// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.2.5
import (
"bytes"
"fmt"
"net"
"github.com/jcmturner/gofork/encoding/asn1"
"gopkg.in/jcmturner/gokrb5.v5/iana/addrtype"
)
// HostAddresses implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.5
type HostAddresses []HostAddress
// HostAddress implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.5
type HostAddress struct {
AddrType int32 `asn1:"explicit,tag:0"`
Address []byte `asn1:"explicit,tag:1"`
}
// GetHostAddress returns a HostAddress struct from a string in the format :
func GetHostAddress(s string) (HostAddress, error) {
var h HostAddress
cAddr, _, err := net.SplitHostPort(s)
if err != nil {
return h, fmt.Errorf("invalid format of client address: %v", err)
}
ip := net.ParseIP(cAddr)
hb, err := ip.MarshalText()
if err != nil {
return h, fmt.Errorf("could not marshal client's address into bytes: %v", err)
}
var ht int32
if ip.To4() != nil {
ht = addrtype.IPv4
} else if ip.To16() != nil {
ht = addrtype.IPv6
} else {
return h, fmt.Errorf("could not determine client's address types: %v", err)
}
h = HostAddress{
AddrType: ht,
Address: hb,
}
return h, nil
}
// GetAddress returns a string representation of the HostAddress.
func (h *HostAddress) GetAddress() (string, error) {
var b []byte
_, err := asn1.Unmarshal(h.Address, &b)
return string(b), err
}
// LocalHostAddresses returns a HostAddresses struct for the local machines interface IP addresses.
func LocalHostAddresses() (ha HostAddresses, err error) {
ifs, err := net.Interfaces()
if err != nil {
return
}
for _, iface := range ifs {
if iface.Flags&net.FlagLoopback != 0 || iface.Flags&net.FlagUp == 0 {
// Interface is either loopback of not up
continue
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
var a HostAddress
if ip.To16() == nil {
//neither IPv4 or IPv6
continue
}
if ip.To4() != nil {
//Is IPv4
a.AddrType = addrtype.IPv4
a.Address = ip.To4()
} else {
a.AddrType = addrtype.IPv6
a.Address = ip.To16()
}
ha = append(ha, a)
}
}
return ha, nil
}
// HostAddressesFromNetIPs returns a HostAddresses type from a slice of net.IP
func HostAddressesFromNetIPs(ips []net.IP) (ha HostAddresses) {
for _, ip := range ips {
ha = append(ha, HostAddressFromNetIP(ip))
}
return ha
}
// HostAddressFromNetIP returns a HostAddress type from a net.IP
func HostAddressFromNetIP(ip net.IP) HostAddress {
if ip.To4() != nil {
//Is IPv4
return HostAddress{
AddrType: addrtype.IPv4,
Address: ip.To4(),
}
}
return HostAddress{
AddrType: addrtype.IPv6,
Address: ip.To16(),
}
}
// HostAddressesEqual tests if two HostAddress slices are equal.
func HostAddressesEqual(h, a []HostAddress) bool {
if len(h) != len(a) {
return false
}
for _, e := range a {
var found bool
for _, i := range h {
if e.Equal(i) {
found = true
break
}
}
if !found {
return false
}
}
return true
}
// HostAddressesContains tests if a HostAddress is contained in a HostAddress slice.
func HostAddressesContains(h []HostAddress, a HostAddress) bool {
for _, e := range h {
if e.Equal(a) {
return true
}
}
return false
}
// Equal tests if the HostAddress is equal to another HostAddress provided.
func (h *HostAddress) Equal(a HostAddress) bool {
if h.AddrType != a.AddrType {
return false
}
return bytes.Equal(h.Address, a.Address)
}
// Contains tests if a HostAddress is contained within the HostAddresses struct.
func (h *HostAddresses) Contains(a HostAddress) bool {
for _, e := range *h {
if e.Equal(a) {
return true
}
}
return false
}
// Equal tests if a HostAddress slice is equal to the HostAddresses struct.
func (h *HostAddresses) Equal(a []HostAddress) bool {
if len(*h) != len(a) {
return false
}
for _, e := range a {
if !h.Contains(e) {
return false
}
}
return true
}
golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/KerberosFlags.go 0000664 0000000 0000000 00000003023 13625372257 0024777 0 ustar 00root root 0000000 0000000 package types
// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.2.8
import (
"github.com/jcmturner/gofork/encoding/asn1"
)
// NewKrbFlags returns an ASN1 BitString struct of the right size for KrbFlags.
func NewKrbFlags() asn1.BitString {
f := asn1.BitString{}
f.Bytes = make([]byte, 4)
f.BitLength = len(f.Bytes) * 8
return f
}
// SetFlags sets the flags of an ASN1 BitString.
func SetFlags(f *asn1.BitString, j []int) {
for _, i := range j {
SetFlag(f, i)
}
}
// SetFlag sets a flag in an ASN1 BitString.
func SetFlag(f *asn1.BitString, i int) {
for l := len(f.Bytes); l < 4; l++ {
(*f).Bytes = append((*f).Bytes, byte(0))
(*f).BitLength = len((*f).Bytes) * 8
}
//Which byte?
b := int(i / 8)
//Which bit in byte
p := uint(7 - (i - 8*b))
(*f).Bytes[b] = (*f).Bytes[b] | (1 << p)
}
// UnsetFlags unsets flags in an ASN1 BitString.
func UnsetFlags(f *asn1.BitString, j []int) {
for _, i := range j {
UnsetFlag(f, i)
}
}
// UnsetFlag unsets a flag in an ASN1 BitString.
func UnsetFlag(f *asn1.BitString, i int) {
for l := len(f.Bytes); l < 4; l++ {
(*f).Bytes = append((*f).Bytes, byte(0))
(*f).BitLength = len((*f).Bytes) * 8
}
//Which byte?
b := int(i / 8)
//Which bit in byte
p := uint(7 - (i - 8*b))
(*f).Bytes[b] = (*f).Bytes[b] &^ (1 << p)
}
// IsFlagSet tests if a flag is set in the ASN1 BitString.
func IsFlagSet(f *asn1.BitString, i int) bool {
//Which byte?
b := int(i / 8)
//Which bit in byte
p := uint(7 - (i - 8*b))
if (*f).Bytes[b]&(1<